Просмотр исходного кода

event-graph: Implement deterministic genesis event creation with variable rotation time.

parazyd 2 лет назад
Родитель
Сommit
215855335f
3 измененных файлов с 114 добавлено и 18 удалено
  1. 58 7
      src/event_graph2/mod.rs
  2. 8 11
      src/event_graph2/tests.rs
  3. 48 0
      src/event_graph2/util.rs

+ 58 - 7
src/event_graph2/mod.rs

@@ -24,9 +24,9 @@ use std::{
 use async_recursion::async_recursion;
 use darkfi_serial::{deserialize_async, serialize_async};
 use num_bigint::BigUint;
-use smol::lock::RwLock;
+use smol::{lock::RwLock, Executor};
 
-use crate::{net::P2pPtr, Result};
+use crate::{net::P2pPtr, util::time::Timestamp, Result};
 
 /// An event graph event
 pub mod event;
@@ -35,9 +35,15 @@ pub use event::Event;
 /// P2P protocol implementation for the Event Graph
 pub mod proto;
 
+/// Utility functions
+mod util;
+use util::{days_since, DAY};
+
 #[cfg(test)]
 mod tests;
 
+/// Initial genesis timestamp (07 Sep 2023, 00:00:00 UTC)
+const INITIAL_GENESIS: u64 = 1694044800;
 /// The number of parents an event is supposed to have.
 const N_EVENT_PARENTS: usize = 5;
 /// Allowed timestamp drift in seconds
@@ -66,17 +72,62 @@ pub struct EventGraph {
     /// or not. Additionally it is also used when we broadcast the
     /// `TipRep` message telling peers about our unreferenced tips.
     broadcasted_ids: RwLock<HashSet<blake3::Hash>>,
+    // DAG Pruning Task
+    //prune_task: StoppableTaskPtr,
 }
 
 impl EventGraph {
-    /// Create a new [`EventGraph`] instance
-    pub fn new(p2p: P2pPtr, sled_db: &sled::Db, dag_tree_name: &str) -> Result<EventGraphPtr> {
+    /// Create a new [`EventGraph`] instance.
+    /// * `days_rotation` marks the lifetime of the DAG before it's pruned.
+    pub async fn new(
+        p2p: P2pPtr,
+        sled_db: &sled::Db,
+        dag_tree_name: &str,
+        days_rotation: u64,
+        ex: Arc<Executor<'_>>,
+    ) -> Result<EventGraphPtr> {
         let dag = sled_db.open_tree(dag_tree_name)?;
         let unreferenced_tips = RwLock::new(HashSet::new());
         let last_event = RwLock::new(NULL_ID);
         let broadcasted_ids = RwLock::new(HashSet::new());
 
-        Ok(Arc::new(Self { p2p, dag, unreferenced_tips, last_event, broadcasted_ids }))
+        let self_ = Arc::new(Self {
+            p2p,
+            dag: dag.clone(),
+            unreferenced_tips,
+            last_event,
+            broadcasted_ids,
+        });
+
+        // Create the current genesis event based on the `days_rotation`
+        let current_genesis = Self::generate_genesis(days_rotation);
+
+        // Check if we have it in our DAG.
+        // If not, we can prune the DAG and insert this new genesis event.
+        if !dag.contains_key(current_genesis.id().as_bytes())? {
+            dag.clear()?;
+            self_.dag_insert(&current_genesis).await?;
+        }
+
+        Ok(self_)
+    }
+
+    /// Generate a deterministic genesis event corresponding to the DAG's configuration.
+    fn generate_genesis(days_rotation: u64) -> Event {
+        // First check how many days passed since initial genesis.
+        let days_passed = days_since(INITIAL_GENESIS);
+
+        // Calculate the number of days_rotation intervals since INITIAL_GENESIS
+        let rotations_since_genesis = days_passed / days_rotation;
+
+        // Calculate the timestamp of the most recent event
+        let timestamp = INITIAL_GENESIS + (rotations_since_genesis * days_rotation * DAY as u64);
+
+        Event {
+            timestamp: Timestamp(timestamp),
+            content: vec![0x47, 0x45, 0x4e, 0x45, 0x53, 0x49, 0x53],
+            parents: [NULL_ID; N_EVENT_PARENTS],
+        }
     }
 
     /// Sync the DAG from connected peers
@@ -98,8 +149,8 @@ impl EventGraph {
         todo!()
     }
 
-    /// Prune the DAG
-    pub async fn dag_prune(&self) {
+    /// Spawn a background task periodically pruning the DAG.
+    async fn dag_prune(&self) {
         // The DAG should periodically be pruned. This can be a configurable
         // parameter. By pruning, we should deterministically replace the
         // genesis event (can use a deterministic timestamp) and drop everything

+ 8 - 11
src/event_graph2/tests.rs

@@ -26,11 +26,10 @@ use url::Url;
 use crate::{
     event_graph2::{
         proto::{EventPut, ProtocolEventGraph},
-        Event, EventGraph, NULL_ID, N_EVENT_PARENTS,
+        Event, EventGraph, NULL_ID,
     },
     net::{P2p, Settings, SESSION_ALL},
     system::sleep,
-    util::time::Timestamp,
 };
 
 /// Number of nodes to spawn
@@ -82,11 +81,7 @@ async fn eventgraph_propagation_real(ex: Arc<Executor<'static>>) {
     let mut eg_instances = vec![];
     let mut rng = rand::thread_rng();
 
-    let genesis_event = Event {
-        timestamp: Timestamp::current_time(),
-        content: vec![0xff, 0xba, 0xfe, 0xf1],
-        parents: [NULL_ID; N_EVENT_PARENTS],
-    };
+    let mut genesis_event_id = NULL_ID;
 
     // Initialize the nodes
     for i in 0..N_NODES {
@@ -113,11 +108,13 @@ async fn eventgraph_propagation_real(ex: Arc<Executor<'static>>) {
 
         let p2p = P2p::new(settings, ex.clone()).await;
         let sled_db = sled::Config::new().temporary(true).open().unwrap();
-        let event_graph = EventGraph::new(p2p.clone(), &sled_db, "dag").unwrap();
+        let event_graph =
+            EventGraph::new(p2p.clone(), &sled_db, "dag", 1, ex.clone()).await.unwrap();
         let event_graph_ = event_graph.clone();
 
-        // Everyone initializes the event graph with a genesis event.
-        event_graph.dag_insert(&genesis_event).await.unwrap();
+        if genesis_event_id == NULL_ID {
+            genesis_event_id = *event_graph.last_event.read().await;
+        }
 
         // Register the P2P protocols
         let registry = p2p.protocol_registry();
@@ -213,7 +210,7 @@ async fn eventgraph_propagation_real(ex: Arc<Executor<'static>>) {
             i,
             order,
             value,
-            genesis_event.id()
+            genesis_event_id,
         );
     }
 

+ 48 - 0
src/event_graph2/util.rs

@@ -0,0 +1,48 @@
+/* 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 std::time::UNIX_EPOCH;
+
+/// Seconds in a day
+pub(super) const DAY: i64 = 86400;
+
+/// Calculate the midnight timestamp given a number of days.
+/// If `days` is 0, calculate the midnight timestamp of today.
+pub(super) fn midnight_timestamp(days: i64) -> u64 {
+    // Get current time
+    let now = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;
+
+    // Find the timestamp for the midnight of the current day
+    let cur_midnight = (now / DAY) * DAY;
+
+    // Adjust for days_from_now
+    (cur_midnight + (DAY * days)) as u64
+}
+
+/// Calculate the number of days since a given midnight timestamp.
+pub(super) fn days_since(midnight_ts: u64) -> u64 {
+    // Get current time
+    let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
+
+    // Calculate the difference between the current timestamp
+    // and the given midnight timestamp
+    let elapsed_seconds = now - midnight_ts;
+
+    // Convert the elapsed seconds into days
+    elapsed_seconds / DAY as u64
+}