diff --git a/src/event_graph2/event.rs b/src/event_graph2/event.rs new file mode 100644 index 000000000..dcb436bdc --- /dev/null +++ b/src/event_graph2/event.rs @@ -0,0 +1,108 @@ +/* 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 . + */ + +use std::collections::HashSet; + +use darkfi_serial::{async_trait, Encodable, SerialDecodable, SerialEncodable}; + +use super::{EventGraphPtr, EVENT_TIME_DRIFT, NULL_ID, N_EVENT_PARENTS}; +use crate::util::time::Timestamp; + +/// Representation of an event in the Event Graph +#[derive(Debug, Clone, SerialEncodable, SerialDecodable)] +pub struct Event { + /// Timestamp of the event + pub(super) timestamp: Timestamp, + /// Content of the event + pub(super) content: Vec, + /// Parent nodes in the event DAG + pub(super) parents: [blake3::Hash; N_EVENT_PARENTS], +} + +impl Event { + /// Create a new event with the given data and an [`EventGraph`] reference. + /// The timestamp of the event will be the current time, and the parents + /// will be `N_EVENT_PARENTS` from the current event graph unreferenced tips. + /// The parents can also include NULL, but this should be handled by the rest + /// of the codebase. + pub async fn new(data: Vec, event_graph: EventGraphPtr) -> Self { + Self { + timestamp: Timestamp::current_time(), + content: data, + parents: event_graph.get_unreferenced_tips().await, + } + } + + /// Hash the [`Event`] to retrieve its ID + pub fn id(&self) -> blake3::Hash { + let mut hasher = blake3::Hasher::new(); + self.timestamp.encode(&mut hasher).unwrap(); + self.content.encode(&mut hasher).unwrap(); + self.parents.encode(&mut hasher).unwrap(); + hasher.finalize() + } + + /* + /// Check if an [`Event`] is considered too old. + fn is_too_old(&self) -> bool { + self.timestamp.0 < Timestamp::current_time().0 - ORPHAN_AGE_LIMIT + } + */ + + /// Validate a new event for the correct layout and enforce relevant age, + /// assuming some possibility for a time drift. + pub fn validate(&self) -> bool { + // Let's not bother with empty events + if self.content.is_empty() { + return false + } + + // Check if the event is too old or too new + let now = Timestamp::current_time().0; + let too_old = self.timestamp.0 < now - EVENT_TIME_DRIFT; + let too_new = self.timestamp.0 > now + EVENT_TIME_DRIFT; + + if too_old || too_new { + return false + } + + // Validate the parents. We have to check that at least one parent + // is not NULL, that the parent does not recursively reference the + // event, and that no two parents are the same. + let mut seen = HashSet::new(); + let self_id = self.id(); + + for parent_id in self.parents.iter() { + if parent_id == &NULL_ID { + continue + } + + if parent_id == &self_id { + return false + } + + if seen.contains(parent_id) { + return false + } + + seen.insert(parent_id); + } + + !seen.is_empty() + } +} diff --git a/src/event_graph2/mod.rs b/src/event_graph2/mod.rs index 249479a18..9d6e3c012 100644 --- a/src/event_graph2/mod.rs +++ b/src/event_graph2/mod.rs @@ -22,12 +22,14 @@ use std::{ }; use async_recursion::async_recursion; -use darkfi_serial::{ - async_trait, deserialize_async, serialize_async, Encodable, SerialDecodable, SerialEncodable, -}; +use darkfi_serial::{deserialize_async, serialize_async}; use smol::lock::RwLock; -use crate::{net::P2pPtr, util::time::Timestamp, Result}; +use crate::{net::P2pPtr, Result}; + +/// An event graph event +pub mod event; +pub use event::Event; /// P2P protocol implementation for the Event Graph pub mod proto; @@ -155,84 +157,3 @@ impl EventGraph { ordered_events.push_front(event_id); } } - -/// Representation of an event in the Event Graph -#[derive(Debug, Clone, SerialEncodable, SerialDecodable)] -pub struct Event { - /// Timestamp of the event - timestamp: Timestamp, - /// Content of the event - content: Vec, - /// Parent nodes in the event DAG - parents: [blake3::Hash; N_EVENT_PARENTS], -} - -impl Event { - /// Create a new event with the given data and an [`EventGraph`] reference. - /// The timestamp of the event will be the current time, and the parents - /// will be `N_EVENT_PARENTS` from the current event graph unreferenced tips. - pub async fn new(data: Vec, event_graph: EventGraphPtr) -> Self { - Self { - timestamp: Timestamp::current_time(), - content: data, - parents: event_graph.get_unreferenced_tips().await, - } - } - - /// Hash the [`Event`] to retrieve its ID - pub fn id(&self) -> blake3::Hash { - let mut hasher = blake3::Hasher::new(); - self.timestamp.encode(&mut hasher).unwrap(); - self.content.encode(&mut hasher).unwrap(); - self.parents.encode(&mut hasher).unwrap(); - hasher.finalize() - } - - /* - /// Check if an [`Event`] is considered too old. - fn is_too_old(&self) -> bool { - self.timestamp.0 < Timestamp::current_time().0 - ORPHAN_AGE_LIMIT - } - */ - - /// Validate a new event for the correct layout. - pub fn validate(&self) -> bool { - // Let's not bother with empty events - if self.content.is_empty() { - return false - } - - // Check if the event is too old or too new - let now = Timestamp::current_time().0; - let too_old = self.timestamp.0 < now - EVENT_TIME_DRIFT; - let too_new = self.timestamp.0 > now + EVENT_TIME_DRIFT; - - if too_old || too_new { - return false - } - - // Validate the parents. We have to check that at least one parent - // is not NULL, that the parent does not recursively reference the - // event, and that no two parents are the same. - let mut seen = HashSet::new(); - let self_id = self.id(); - - for parent_id in self.parents.iter() { - if parent_id == &NULL_ID { - continue - } - - if parent_id == &self_id { - return false - } - - if seen.contains(parent_id) { - return false - } - - seen.insert(parent_id) - } - - !seen.is_empty() - } -}