darkfi/event_graph/
event.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 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
19use std::{collections::HashSet, time::UNIX_EPOCH};
20
21use darkfi_serial::{async_trait, deserialize_async, Encodable, SerialDecodable, SerialEncodable};
22use sled_overlay::{sled, SledTreeOverlay};
23
24use crate::Result;
25
26use super::{
27    util::next_rotation_timestamp, EventGraph, EVENT_TIME_DRIFT, INITIAL_GENESIS, NULL_ID,
28    N_EVENT_PARENTS,
29};
30
31/// Representation of an event in the Event Graph
32#[derive(Debug, Clone, PartialEq, SerialEncodable, SerialDecodable)]
33pub struct Event {
34    /// Timestamp of the event in whole seconds
35    pub timestamp: u64,
36    /// Content of the event
37    pub content: Vec<u8>,
38    /// Parent nodes in the event DAG
39    pub parents: [blake3::Hash; N_EVENT_PARENTS],
40    /// DAG layer index of the event
41    pub layer: u64,
42}
43
44impl Event {
45    /// Create a new event with the given data and an [`EventGraph`] reference.
46    /// The timestamp of the event will be the current time, and the parents
47    /// will be `N_EVENT_PARENTS` from the current event graph unreferenced tips.
48    /// The parents can also include NULL, but this should be handled by the rest
49    /// of the codebase.
50    pub async fn new(data: Vec<u8>, event_graph: &EventGraph) -> Self {
51        let (layer, parents) = event_graph.get_next_layer_with_parents().await;
52        Self {
53            timestamp: UNIX_EPOCH.elapsed().unwrap().as_millis() as u64,
54            content: data,
55            parents,
56            layer,
57        }
58    }
59
60    /// Same as `Event::new()` but allows specifying the timestamp explicitly.
61    pub async fn with_timestamp(timestamp: u64, data: Vec<u8>, event_graph: &EventGraph) -> Self {
62        let (layer, parents) = event_graph.get_next_layer_with_parents().await;
63        Self { timestamp, content: data, parents, layer }
64    }
65
66    /// Hash the [`Event`] to retrieve its ID
67    pub fn id(&self) -> blake3::Hash {
68        let mut hasher = blake3::Hasher::new();
69        self.timestamp.encode(&mut hasher).unwrap();
70        self.content.encode(&mut hasher).unwrap();
71        self.parents.encode(&mut hasher).unwrap();
72        self.layer.encode(&mut hasher).unwrap();
73        hasher.finalize()
74    }
75
76    /// Return a reference to the event's content
77    pub fn content(&self) -> &[u8] {
78        &self.content
79    }
80
81    /*
82    /// Check if an [`Event`] is considered too old.
83    fn is_too_old(&self) -> bool {
84        self.timestamp < UNIX_EPOCH.elapsed().unwrap().as_secs() - ORPHAN_AGE_LIMIT
85    }
86    */
87
88    /// Fully validate an event for the correct layout against provided
89    /// DAG [`sled::Tree`] reference and enforce relevant age, assuming
90    /// some possibility for a time drift. Optionally, provide an overlay
91    /// to use that instead of actual referenced DAG.
92    pub async fn validate(
93        &self,
94        dag: &sled::Tree,
95        genesis_timestamp: u64,
96        days_rotation: u64,
97        overlay: Option<&SledTreeOverlay>,
98    ) -> Result<bool> {
99        // Let's not bother with empty events
100        if self.content.is_empty() {
101            return Ok(false)
102        }
103
104        // Check if the event timestamp is after genesis timestamp
105        if self.timestamp < genesis_timestamp - EVENT_TIME_DRIFT {
106            return Ok(false)
107        }
108
109        // If a rotation has been set, check if the event timestamp
110        // is after the next genesis timestamp
111        if days_rotation > 0 {
112            let next_genesis_timestamp = next_rotation_timestamp(INITIAL_GENESIS, days_rotation);
113            if self.timestamp > next_genesis_timestamp + EVENT_TIME_DRIFT {
114                return Ok(false)
115            }
116        }
117
118        // Validate the parents. We have to check that at least one parent
119        // is not NULL, that the parents exist, that no two parents are the
120        // same, and that the parent exists in previous layers, to prevent
121        // recursive references(circles).
122        let mut seen = HashSet::new();
123        let self_id = self.id();
124
125        for parent_id in self.parents.iter() {
126            if parent_id == &NULL_ID {
127                continue
128            }
129
130            if parent_id == &self_id {
131                return Ok(false)
132            }
133
134            if seen.contains(parent_id) {
135                return Ok(false)
136            }
137
138            let parent_bytes = if let Some(overlay) = overlay {
139                overlay.get(parent_id.as_bytes())?
140            } else {
141                dag.get(parent_id.as_bytes())?
142            };
143            if parent_bytes.is_none() {
144                return Ok(false)
145            }
146
147            let parent: Event = deserialize_async(&parent_bytes.unwrap()).await?;
148            if self.layer <= parent.layer {
149                return Ok(false)
150            }
151
152            seen.insert(parent_id);
153        }
154
155        Ok(!seen.is_empty())
156    }
157
158    /// Fully validate an event for the correct layout against provided
159    /// [`EventGraph`] reference and enforce relevant age, assuming some
160    /// possibility for a time drift.
161    pub async fn dag_validate(&self, event_graph: &EventGraph) -> Result<bool> {
162        // Grab genesis timestamp
163        let genesis_timestamp = event_graph.current_genesis.read().await.timestamp;
164
165        // Perform validation
166        self.validate(&event_graph.dag, genesis_timestamp, event_graph.days_rotation, None).await
167    }
168
169    /// Validate a new event for the correct layout and enforce relevant age,
170    /// assuming some possibility for a time drift.
171    /// Note: This validation does *NOT* check for recursive references(circles),
172    /// and should be used as a first quick check.
173    pub fn validate_new(&self) -> bool {
174        // Let's not bother with empty events
175        if self.content.is_empty() {
176            return false
177        }
178
179        // Check if the event is too old or too new
180        let now = UNIX_EPOCH.elapsed().unwrap().as_millis() as u64;
181        let too_old = self.timestamp < now - EVENT_TIME_DRIFT;
182        let too_new = self.timestamp > now + EVENT_TIME_DRIFT;
183        if too_old || too_new {
184            return false
185        }
186
187        // Validate the parents. We have to check that at least one parent
188        // is not NULL and that no two parents are the same.
189        let mut seen = HashSet::new();
190        let self_id = self.id();
191
192        for parent_id in self.parents.iter() {
193            if parent_id == &NULL_ID {
194                continue
195            }
196
197            if parent_id == &self_id {
198                return false
199            }
200
201            if seen.contains(parent_id) {
202                return false
203            }
204
205            seen.insert(parent_id);
206        }
207
208        !seen.is_empty()
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use std::sync::Arc;
215
216    use smol::Executor;
217
218    use crate::{
219        event_graph::{EventGraph, EventGraphPtr},
220        net::{P2p, Settings},
221    };
222
223    use super::*;
224
225    async fn make_event_graph() -> Result<EventGraphPtr> {
226        let ex = Arc::new(Executor::new());
227        let p2p = P2p::new(Settings::default(), ex.clone()).await?;
228        let sled_db = sled::Config::new().temporary(true).open().unwrap();
229        EventGraph::new(p2p, sled_db, "/tmp".into(), false, "dag", 1, ex).await
230    }
231
232    #[test]
233    fn event_is_valid() -> Result<()> {
234        smol::block_on(async {
235            // Generate a dummy event graph
236            let event_graph = make_event_graph().await?;
237
238            // Create a new valid event
239            let valid_event = Event::new(vec![1u8], &event_graph).await;
240
241            // Validate our test Event struct
242            assert!(valid_event.dag_validate(&event_graph).await?);
243
244            // Thanks for reading
245            Ok(())
246        })
247    }
248
249    #[test]
250    fn invalid_events() -> Result<()> {
251        smol::block_on(async {
252            // Generate a dummy event graph
253            let event_graph = make_event_graph().await?;
254
255            // Create a new valid event
256            let valid_event = Event::new(vec![1u8], &event_graph).await;
257
258            let mut event_empty_content = valid_event.clone();
259            event_empty_content.content = vec![];
260            assert!(!event_empty_content.dag_validate(&event_graph).await?);
261
262            let mut event_timestamp_too_old = valid_event.clone();
263            event_timestamp_too_old.timestamp = 0;
264            assert!(!event_timestamp_too_old.dag_validate(&event_graph).await?);
265
266            let mut event_timestamp_too_new = valid_event.clone();
267            event_timestamp_too_new.timestamp = u64::MAX;
268            assert!(!event_timestamp_too_new.dag_validate(&event_graph).await?);
269
270            let mut event_duplicated_parents = valid_event.clone();
271            event_duplicated_parents.parents[1] = valid_event.parents[0];
272            assert!(!event_duplicated_parents.dag_validate(&event_graph).await?);
273
274            let mut event_null_parents = valid_event.clone();
275            let all_null_parents = [NULL_ID, NULL_ID, NULL_ID, NULL_ID, NULL_ID];
276            event_null_parents.parents = all_null_parents;
277            assert!(!event_null_parents.dag_validate(&event_graph).await?);
278
279            let mut event_same_layer_as_parents = valid_event.clone();
280            event_same_layer_as_parents.layer = 0;
281            assert!(!event_same_layer_as_parents.dag_validate(&event_graph).await?);
282
283            // Thanks for reading
284            Ok(())
285        })
286    }
287}