lib.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 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. use darkfi::{
  19. event_graph::{
  20. util::{generate_genesis, millis_until_next_rotation, next_rotation_timestamp},
  21. Event, GENESIS_CONTENTS, INITIAL_GENESIS, NULL_ID, N_EVENT_PARENTS,
  22. },
  23. system::{msleep, Publisher, PublisherPtr, StoppableTask, StoppableTaskPtr},
  24. Error, Result,
  25. };
  26. use darkfi_serial::{
  27. async_trait, deserialize_async, serialize_async, SerialDecodable, SerialEncodable,
  28. };
  29. use tracing::{debug, error, info};
  30. use sled_overlay::{sled, SledTreeOverlay};
  31. use smol::{
  32. lock::{OnceCell, RwLock},
  33. Executor,
  34. };
  35. use std::{
  36. collections::{BTreeMap, HashSet},
  37. sync::Arc,
  38. };
  39. pub const PROTOCOL_VERSION: u32 = 1;
  40. /// Atomic pointer to an [`EventGraph`] instance.
  41. pub type LocalEventGraphPtr = Arc<LocalEventGraph>;
  42. pub struct LocalEventGraph {
  43. /// Sled tree containing the DAG
  44. pub dag: sled::Tree,
  45. /// The set of unreferenced DAG tips
  46. pub unreferenced_tips: RwLock<BTreeMap<u64, HashSet<blake3::Hash>>>,
  47. /// A `HashSet` containg event IDs and their 1-level parents.
  48. /// These come from the events we've sent out using `EventPut`.
  49. /// They are used with `EventReq` to decide if we should reply
  50. /// or not. Additionally it is also used when we broadcast the
  51. /// `TipRep` message telling peers about our unreferenced tips.
  52. broadcasted_ids: RwLock<HashSet<blake3::Hash>>,
  53. /// DAG Pruning Task
  54. pub prune_task: OnceCell<StoppableTaskPtr>,
  55. /// Event publisher, this notifies whenever an event is
  56. /// inserted into the DAG
  57. pub event_pub: PublisherPtr<Event>,
  58. /// Current genesis event
  59. pub current_genesis: RwLock<Event>,
  60. /// Currently configured DAG rotation, in days
  61. pub days_rotation: u64,
  62. /// Flag signalling DAG has finished initial sync
  63. pub synced: RwLock<bool>,
  64. /// Enable graph debugging
  65. pub deg_enabled: RwLock<bool>,
  66. }
  67. impl LocalEventGraph {
  68. pub async fn new(
  69. sled_db: sled::Db,
  70. dag_tree_name: &str,
  71. days_rotation: u64,
  72. ex: Arc<Executor<'_>>,
  73. ) -> Result<LocalEventGraphPtr> {
  74. let dag = sled_db.open_tree(dag_tree_name)?;
  75. let unreferenced_tips = RwLock::new(BTreeMap::new());
  76. let broadcasted_ids = RwLock::new(HashSet::new());
  77. let event_pub = Publisher::new();
  78. // Create the current genesis event based on the `days_rotation`
  79. let current_genesis = generate_genesis(days_rotation);
  80. let self_ = Arc::new(Self {
  81. dag: dag.clone(),
  82. unreferenced_tips,
  83. broadcasted_ids,
  84. prune_task: OnceCell::new(),
  85. event_pub,
  86. current_genesis: RwLock::new(current_genesis.clone()),
  87. days_rotation,
  88. synced: RwLock::new(false),
  89. deg_enabled: RwLock::new(false),
  90. });
  91. // Check if we have it in our DAG.
  92. // If not, we can prune the DAG and insert this new genesis event.
  93. if !dag.contains_key(current_genesis.id().as_bytes())? {
  94. info!(
  95. target: "event_graph::new()",
  96. "[EVENTGRAPH] DAG does not contain current genesis, pruning existing data",
  97. );
  98. self_.dag_prune(current_genesis).await?;
  99. }
  100. // Find the unreferenced tips in the current DAG state.
  101. *self_.unreferenced_tips.write().await = self_.find_unreferenced_tips().await;
  102. // Spawn the DAG pruning task
  103. if days_rotation > 0 {
  104. let prune_task = StoppableTask::new();
  105. let _ = self_.prune_task.set(prune_task.clone()).await;
  106. prune_task.clone().start(
  107. self_.clone().dag_prune_task(days_rotation),
  108. |_| async move {
  109. info!(target: "event_graph::_handle_stop()", "[EVENTGRAPH] Prune task stopped, flushing sled")
  110. },
  111. Error::DetachedTaskStopped,
  112. ex.clone(),
  113. );
  114. }
  115. Ok(self_)
  116. }
  117. async fn dag_prune(&self, genesis_event: Event) -> Result<()> {
  118. debug!(target: "event_graph::dag_prune()", "Pruning DAG...");
  119. // Acquire exclusive locks to unreferenced_tips, broadcasted_ids and
  120. // current_genesis while this operation is happening. We do this to
  121. // ensure that during the pruning operation, no other operations are
  122. // able to access the intermediate state which could lead to producing
  123. // the wrong state after pruning.
  124. let mut unreferenced_tips = self.unreferenced_tips.write().await;
  125. let mut broadcasted_ids = self.broadcasted_ids.write().await;
  126. let mut current_genesis = self.current_genesis.write().await;
  127. // Atomically clear the DAG and write the new genesis event.
  128. let mut batch = sled::Batch::default();
  129. for key in self.dag.iter().keys() {
  130. batch.remove(key.unwrap());
  131. }
  132. batch.insert(genesis_event.id().as_bytes(), serialize_async(&genesis_event).await);
  133. debug!(target: "event_graph::dag_prune()", "Applying batch...");
  134. if let Err(e) = self.dag.apply_batch(batch) {
  135. panic!("Failed pruning DAG, sled apply_batch error: {}", e);
  136. }
  137. // Clear unreferenced tips and bcast ids
  138. *unreferenced_tips = BTreeMap::new();
  139. unreferenced_tips.insert(0, HashSet::from([genesis_event.id()]));
  140. *current_genesis = genesis_event;
  141. *broadcasted_ids = HashSet::new();
  142. drop(unreferenced_tips);
  143. drop(broadcasted_ids);
  144. drop(current_genesis);
  145. debug!(target: "event_graph::dag_prune()", "DAG pruned successfully");
  146. Ok(())
  147. }
  148. /// Background task periodically pruning the DAG.
  149. async fn dag_prune_task(self: Arc<Self>, days_rotation: u64) -> Result<()> {
  150. // The DAG should periodically be pruned. This can be a configurable
  151. // parameter. By pruning, we should deterministically replace the
  152. // genesis event (can use a deterministic timestamp) and drop everything
  153. // in the DAG, leaving just the new genesis event.
  154. debug!(target: "event_graph::dag_prune_task()", "Spawned background DAG pruning task");
  155. loop {
  156. // Find the next rotation timestamp:
  157. let next_rotation = next_rotation_timestamp(INITIAL_GENESIS, days_rotation);
  158. // Prepare the new genesis event
  159. let current_genesis = Event {
  160. timestamp: next_rotation,
  161. content: GENESIS_CONTENTS.to_vec(),
  162. parents: [NULL_ID; N_EVENT_PARENTS],
  163. layer: 0,
  164. };
  165. // Sleep until it's time to rotate.
  166. let s = millis_until_next_rotation(next_rotation);
  167. debug!(target: "event_graph::dag_prune_task()", "Sleeping {}s until next DAG prune", s);
  168. msleep(s).await;
  169. debug!(target: "event_graph::dag_prune_task()", "Rotation period reached");
  170. // Trigger DAG prune
  171. self.dag_prune(current_genesis).await?;
  172. }
  173. }
  174. /// Find the unreferenced tips in the current DAG state, mapped by their layers.
  175. async fn find_unreferenced_tips(&self) -> BTreeMap<u64, HashSet<blake3::Hash>> {
  176. // First get all the event IDs
  177. let mut tips = HashSet::new();
  178. for iter_elem in self.dag.iter() {
  179. let (id, _) = iter_elem.unwrap();
  180. let id = blake3::Hash::from_bytes((&id as &[u8]).try_into().unwrap());
  181. tips.insert(id);
  182. }
  183. // Iterate again to find unreferenced IDs
  184. for iter_elem in self.dag.iter() {
  185. let (_, event) = iter_elem.unwrap();
  186. let event: Event = deserialize_async(&event).await.unwrap();
  187. for parent in event.parents.iter() {
  188. tips.remove(parent);
  189. }
  190. }
  191. // Build the layers map
  192. let mut map: BTreeMap<u64, HashSet<blake3::Hash>> = BTreeMap::new();
  193. for tip in tips {
  194. let event = self.dag_get(&tip).await.unwrap().unwrap();
  195. if let Some(layer_tips) = map.get_mut(&event.layer) {
  196. layer_tips.insert(tip);
  197. } else {
  198. let mut layer_tips = HashSet::new();
  199. layer_tips.insert(tip);
  200. map.insert(event.layer, layer_tips);
  201. }
  202. }
  203. map
  204. }
  205. pub async fn dag_insert(&self, events: &[Event]) -> Result<Vec<blake3::Hash>> {
  206. // Sanity check
  207. if events.is_empty() {
  208. return Ok(vec![])
  209. }
  210. // Acquire exclusive locks to `unreferenced_tips and broadcasted_ids`
  211. let mut unreferenced_tips = self.unreferenced_tips.write().await;
  212. let mut broadcasted_ids = self.broadcasted_ids.write().await;
  213. // Here we keep the IDs to return
  214. let mut ids = Vec::with_capacity(events.len());
  215. // Create an overlay over the DAG tree
  216. let mut overlay = SledTreeOverlay::new(&self.dag);
  217. // Grab genesis timestamp
  218. let genesis_timestamp = self.current_genesis.read().await.timestamp;
  219. // Iterate over given events to validate them and
  220. // write them to the overlay
  221. for event in events {
  222. let event_id = event.id();
  223. debug!(
  224. target: "event_graph::dag_insert()",
  225. "Inserting event {} into the DAG", event_id,
  226. );
  227. if !event
  228. .validate(&self.dag, genesis_timestamp, self.days_rotation, Some(&overlay))
  229. .await?
  230. {
  231. error!(target: "event_graph::dag_insert()", "Event {} is invalid!", event_id);
  232. return Err(Error::EventIsInvalid)
  233. }
  234. let event_se = serialize_async(event).await;
  235. // Add the event to the overlay
  236. overlay.insert(event_id.as_bytes(), &event_se)?;
  237. // Note down the event ID to return
  238. ids.push(event_id);
  239. }
  240. // Aggregate changes into a single batch
  241. let batch = overlay.aggregate().unwrap();
  242. // Atomically apply the batch.
  243. // Panic if something is corrupted.
  244. if let Err(e) = self.dag.apply_batch(batch) {
  245. panic!("Failed applying dag_insert batch to sled: {}", e);
  246. }
  247. // Iterate over given events to update references and
  248. // send out notifications about them
  249. for event in events {
  250. let event_id = event.id();
  251. // Update the unreferenced DAG tips set
  252. debug!(
  253. target: "event_graph::dag_insert()",
  254. "Event {} parents {:#?}", event_id, event.parents,
  255. );
  256. for parent_id in event.parents.iter() {
  257. if parent_id != &NULL_ID {
  258. debug!(
  259. target: "event_graph::dag_insert()",
  260. "Removing {} from unreferenced_tips", parent_id,
  261. );
  262. // Iterate over unreferenced tips in previous layers
  263. // and remove the parent
  264. // NOTE: this might be too exhaustive, but the
  265. // assumption is that previous layers unreferenced
  266. // tips will be few.
  267. for (layer, tips) in unreferenced_tips.iter_mut() {
  268. if layer >= &event.layer {
  269. continue
  270. }
  271. tips.remove(parent_id);
  272. }
  273. broadcasted_ids.insert(*parent_id);
  274. }
  275. }
  276. unreferenced_tips.retain(|_, tips| !tips.is_empty());
  277. debug!(
  278. target: "event_graph::dag_insert()",
  279. "Adding {} to unreferenced tips", event_id,
  280. );
  281. if let Some(layer_tips) = unreferenced_tips.get_mut(&event.layer) {
  282. layer_tips.insert(event_id);
  283. } else {
  284. let mut layer_tips = HashSet::new();
  285. layer_tips.insert(event_id);
  286. unreferenced_tips.insert(event.layer, layer_tips);
  287. }
  288. // Send out notifications about the new event
  289. self.event_pub.notify(event.clone()).await;
  290. }
  291. // Drop the exclusive locks
  292. drop(unreferenced_tips);
  293. drop(broadcasted_ids);
  294. Ok(ids)
  295. }
  296. /// Fetch an event from the DAG
  297. pub async fn dag_get(&self, event_id: &blake3::Hash) -> Result<Option<Event>> {
  298. let Some(bytes) = self.dag.get(event_id.as_bytes())? else { return Ok(None) };
  299. let event: Event = deserialize_async(&bytes).await?;
  300. Ok(Some(event))
  301. }
  302. }
  303. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  304. pub struct VersionMessage {
  305. pub protocol_version: u32,
  306. }
  307. impl VersionMessage {
  308. pub fn new() -> Self {
  309. Self { protocol_version: PROTOCOL_VERSION }
  310. }
  311. }
  312. impl Default for VersionMessage {
  313. fn default() -> Self {
  314. Self::new()
  315. }
  316. }
  317. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  318. pub struct FetchEventsMessage {
  319. pub unref_tips: BTreeMap<u64, HashSet<blake3::Hash>>,
  320. }
  321. impl FetchEventsMessage {
  322. pub fn new(unref_tips: BTreeMap<u64, HashSet<blake3::Hash>>) -> Self {
  323. Self { unref_tips }
  324. }
  325. }
  326. pub const MSG_EVENT: u8 = 1;
  327. pub const MSG_FETCHEVENTS: u8 = 2;
  328. pub const MSG_SENDEVENT: u8 = 3;