mod.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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 std::{
  19. cmp::Ordering,
  20. collections::{HashMap, HashSet, VecDeque},
  21. sync::{
  22. atomic::{AtomicBool, Ordering::SeqCst},
  23. Arc,
  24. },
  25. time::UNIX_EPOCH,
  26. };
  27. use async_recursion::async_recursion;
  28. use darkfi_serial::{deserialize_async, serialize_async};
  29. use log::{debug, error, info};
  30. use num_bigint::BigUint;
  31. use smol::{
  32. lock::{Mutex, RwLock},
  33. Executor,
  34. };
  35. use crate::{
  36. net::P2pPtr,
  37. system::{sleep, timeout::timeout, StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr},
  38. Error, Result,
  39. };
  40. /// An event graph event
  41. pub mod event;
  42. pub use event::Event;
  43. /// P2P protocol implementation for the Event Graph
  44. pub mod proto;
  45. use proto::{EventRep, EventReq, TipRep, TipReq, REPLY_TIMEOUT};
  46. /// Utility functions
  47. mod util;
  48. use util::{days_since, next_rotation_timestamp, DAY};
  49. #[cfg(test)]
  50. mod tests;
  51. /// Initial genesis timestamp (07 Sep 2023, 00:00:00 UTC)
  52. /// Must always be UTC midnight.
  53. const INITIAL_GENESIS: u64 = 1694044800;
  54. /// Genesis event contents
  55. const GENESIS_CONTENTS: &[u8] = &[0x47, 0x45, 0x4e, 0x45, 0x53, 0x49, 0x53];
  56. /// The number of parents an event is supposed to have.
  57. const N_EVENT_PARENTS: usize = 5;
  58. /// Allowed timestamp drift in seconds
  59. const EVENT_TIME_DRIFT: u64 = 60;
  60. /// Null event ID
  61. pub const NULL_ID: blake3::Hash = blake3::Hash::from_bytes([0x00; blake3::OUT_LEN]);
  62. /// Atomic pointer to an [`EventGraph`] instance.
  63. pub type EventGraphPtr = Arc<EventGraph>;
  64. /// An Event Graph instance
  65. pub struct EventGraph {
  66. /// Pointer to the P2P network instance
  67. p2p: P2pPtr,
  68. /// Sled tree containing the DAG
  69. dag: sled::Tree,
  70. /// The set of unreferenced DAG tips
  71. unreferenced_tips: RwLock<HashSet<blake3::Hash>>,
  72. /// A `HashSet` containg event IDs and their 1-level parents.
  73. /// These come from the events we've sent out using `EventPut`.
  74. /// They are used with `EventReq` to decide if we should reply
  75. /// or not. Additionally it is also used when we broadcast the
  76. /// `TipRep` message telling peers about our unreferenced tips.
  77. broadcasted_ids: RwLock<HashSet<blake3::Hash>>,
  78. /// Marker telling us if we consider the DAG synced
  79. dag_synced: AtomicBool,
  80. /// DAG Pruning Task
  81. prune_task: Mutex<Option<StoppableTaskPtr>>,
  82. /// Event subscriber, this notifies whenever an event is
  83. /// inserted into the DAG
  84. pub event_sub: SubscriberPtr<Event>,
  85. }
  86. impl EventGraph {
  87. /// Create a new [`EventGraph`] instance.
  88. /// * `days_rotation` marks the lifetime of the DAG before it's pruned.
  89. pub async fn new(
  90. p2p: P2pPtr,
  91. sled_db: sled::Db,
  92. dag_tree_name: &str,
  93. days_rotation: u64,
  94. ex: Arc<Executor<'_>>,
  95. ) -> Result<EventGraphPtr> {
  96. let dag = sled_db.open_tree(dag_tree_name)?;
  97. let unreferenced_tips = RwLock::new(HashSet::new());
  98. let broadcasted_ids = RwLock::new(HashSet::new());
  99. let event_sub = Subscriber::new();
  100. let self_ = Arc::new(Self {
  101. p2p,
  102. dag: dag.clone(),
  103. unreferenced_tips,
  104. broadcasted_ids,
  105. dag_synced: AtomicBool::new(false),
  106. prune_task: Mutex::new(None),
  107. event_sub,
  108. });
  109. // Create the current genesis event based on the `days_rotation`
  110. let current_genesis = Self::generate_genesis(days_rotation);
  111. // Check if we have it in our DAG.
  112. // If not, we can prune the DAG and insert this new genesis event.
  113. if !dag.contains_key(current_genesis.id().as_bytes())? {
  114. info!(
  115. target: "event_graph::new()",
  116. "[EVENTGRAPH] DAG does not contain current genesis, pruning existing data",
  117. );
  118. dag.clear()?;
  119. self_.dag_insert(current_genesis).await?;
  120. }
  121. // Find the unreferenced tips in the current DAG state.
  122. *self_.unreferenced_tips.write().await = self_.find_unreferenced_tips().await;
  123. // Spawn the DAG pruning task
  124. let self__ = self_.clone();
  125. let prune_task = StoppableTask::new();
  126. *self_.prune_task.lock().await = Some(prune_task.clone());
  127. prune_task.clone().start(
  128. self_.clone().dag_prune(days_rotation),
  129. |_| async move {
  130. self__.clone()._handle_stop(sled_db).await;
  131. },
  132. Error::DetachedTaskStopped,
  133. ex.clone(),
  134. );
  135. Ok(self_)
  136. }
  137. async fn _handle_stop(&self, sled_db: sled::Db) {
  138. info!(target: "event_graph::_handle_stop()", "[EVENTGRAPH] Prune task stopped, flushing sled");
  139. sled_db.flush_async().await.unwrap();
  140. }
  141. /// Generate a deterministic genesis event corresponding to the DAG's configuration.
  142. fn generate_genesis(days_rotation: u64) -> Event {
  143. // Days rotation is u64 except zero
  144. let genesis_days_rotation = if days_rotation == 0 { 1 } else { days_rotation };
  145. // First check how many days passed since initial genesis.
  146. let days_passed = days_since(INITIAL_GENESIS);
  147. // Calculate the number of days_rotation intervals since INITIAL_GENESIS
  148. let rotations_since_genesis = days_passed / genesis_days_rotation;
  149. // Calculate the timestamp of the most recent event
  150. let timestamp =
  151. INITIAL_GENESIS + (rotations_since_genesis * genesis_days_rotation * DAY as u64);
  152. Event { timestamp, content: GENESIS_CONTENTS.to_vec(), parents: [NULL_ID; N_EVENT_PARENTS] }
  153. }
  154. /// Sync the DAG from connected peers
  155. pub async fn dag_sync(&self) -> Result<()> {
  156. // We do an optimistic sync where we ask all our connected peers for
  157. // the DAG tips (unreferenced events) and then we accept the ones we
  158. // see the most times.
  159. // * Compare received tips with local ones, identify which we are missing.
  160. // * Request these from peers
  161. // * Recursively request these backward
  162. //
  163. // Verification:
  164. // * Timestamps should go backwards
  165. // * Cross-check with multiple peers, this means we should request the
  166. // same event from multiple peers and make sure it is the same.
  167. // * Since we should be pruning, if we're not synced after some reasonable
  168. // amount of iterations, these could be faulty peers and we can try again
  169. // from the beginning
  170. // Get references to all our peers.
  171. let channels = self.p2p.channels().await;
  172. let mut communicated_peers = channels.len();
  173. info!(
  174. target: "event_graph::dag_sync()",
  175. "[EVENTGRAPH] Syncing DAG from {} peers...", communicated_peers,
  176. );
  177. // Here we keep track of the tips and how many time we've seen them.
  178. let mut tips: HashMap<blake3::Hash, usize> = HashMap::new();
  179. // Let's first ask all of our peers for their tips and collect them
  180. // in our hashmap above.
  181. for channel in channels.iter() {
  182. let url = channel.address();
  183. let tip_rep_sub = match channel.subscribe_msg::<TipRep>().await {
  184. Ok(v) => v,
  185. Err(e) => {
  186. error!(
  187. target: "event_graph::dag_sync()",
  188. "[EVENTGRAPH] Sync: Couldn't subscribe TipReq for peer {}, skipping ({})",
  189. url, e,
  190. );
  191. communicated_peers -= 1;
  192. continue
  193. }
  194. };
  195. if let Err(e) = channel.send(&TipReq {}).await {
  196. error!(
  197. target: "event_graph::dag_sync()",
  198. "[EVENTGRAPH] Sync: Couldn't contact peer {}, skipping ({})", url, e,
  199. );
  200. communicated_peers -= 1;
  201. continue
  202. };
  203. let peer_tips = match timeout(REPLY_TIMEOUT, tip_rep_sub.receive()).await {
  204. Ok(peer_tips) => peer_tips?,
  205. Err(_) => {
  206. error!(
  207. target: "event_graph::dag_sync()",
  208. "[EVENTGRAPH] Sync: Peer {} didn't reply with tips in time, skipping", url,
  209. );
  210. communicated_peers -= 1;
  211. continue
  212. }
  213. };
  214. let peer_tips = &peer_tips.0;
  215. // Note down the seen tips
  216. for tip in peer_tips {
  217. if let Some(seen_tip) = tips.get_mut(tip) {
  218. *seen_tip += 1;
  219. } else {
  220. tips.insert(*tip, 1);
  221. }
  222. }
  223. }
  224. // After we've communicated all the peers, let's see what happened.
  225. if tips.is_empty() {
  226. error!(
  227. target: "event_graph::dag_sync()",
  228. "[EVENTGRAPH] Sync: Could not find any DAG tips",
  229. );
  230. return Err(Error::DagSyncFailed)
  231. }
  232. // We know the number of peers we've communicated with.
  233. // Arbitrarily, let's not consider events we only got once.
  234. // TODO: This should be more sensible depending on the peer number.
  235. let mut considered_tips = HashSet::new();
  236. for (tip, amount) in tips.iter() {
  237. if amount > &1 {
  238. considered_tips.insert(*tip);
  239. }
  240. }
  241. drop(tips);
  242. // Now begin fetching the events backwards.
  243. let mut missing_parents = vec![];
  244. for tip in considered_tips.iter() {
  245. assert!(tip != &NULL_ID);
  246. if !self.dag.contains_key(tip.as_bytes()).unwrap() {
  247. missing_parents.push(*tip);
  248. }
  249. }
  250. if missing_parents.is_empty() {
  251. info!(target: "event_graph::dag_sync()", "[EVENTGRAPH] DAG synced successfully!");
  252. return Ok(())
  253. }
  254. info!(target: "event_graph::dag_sync()", "[EVENTGRAPH] Fetching events");
  255. let mut received_events = vec![];
  256. while !missing_parents.is_empty() {
  257. for parent_id in missing_parents.clone().iter() {
  258. let mut found_event = false;
  259. for channel in channels.iter() {
  260. let url = channel.address();
  261. debug!(
  262. target: "event_graph::dag_sync()",
  263. "Requesting {} from {}...", parent_id, url,
  264. );
  265. let ev_rep_sub = match channel.subscribe_msg::<EventRep>().await {
  266. Ok(v) => v,
  267. Err(e) => {
  268. error!(
  269. target: "event_graph::dag_sync()",
  270. "[EVENTGRAPH] Sync: Couldn't subscribe EventRep for peer {}, skipping ({})",
  271. url, e,
  272. );
  273. continue
  274. }
  275. };
  276. if let Err(e) = channel.send(&EventReq(*parent_id)).await {
  277. error!(
  278. target: "event_graph::dag_sync()",
  279. "[EVENTGRAPH] Sync: Failed communicating EventReq({}) to {}: {}",
  280. parent_id, url, e,
  281. );
  282. continue
  283. }
  284. let parent = match timeout(REPLY_TIMEOUT, ev_rep_sub.receive()).await {
  285. Ok(parent) => parent,
  286. Err(_) => {
  287. error!(
  288. target: "event_graph::dag_sync()",
  289. "[EVENTGRAPH] Sync: Timeout waiting for parent {} from {}",
  290. parent_id, url,
  291. );
  292. continue
  293. }
  294. };
  295. let parent = match parent {
  296. Ok(v) => v.0.clone(),
  297. Err(e) => {
  298. error!(
  299. target: "event_graph::dag_sync()",
  300. "[EVENTGRAPH] Sync: Failed receiving parent {}: {}",
  301. parent_id, e,
  302. );
  303. continue
  304. }
  305. };
  306. if &parent.id() != parent_id {
  307. error!(
  308. target: "event_graph::dag_sync()",
  309. "[EVENTGRAPH] Sync: Peer {} replied with a wrong event: {}",
  310. url, parent.id(),
  311. );
  312. continue
  313. }
  314. debug!(
  315. target: "event_graph::dag_sync()",
  316. "Got correct parent event {}", parent_id,
  317. );
  318. received_events.push(parent.clone());
  319. let pos = missing_parents.iter().position(|id| id == &parent.id()).unwrap();
  320. missing_parents.remove(pos);
  321. found_event = true;
  322. // See if we have the upper parents
  323. for upper_parent in parent.parents.iter() {
  324. if upper_parent == &NULL_ID {
  325. continue
  326. }
  327. if !self.dag.contains_key(upper_parent.as_bytes()).unwrap() {
  328. debug!(
  329. target: "event_graph::dag_sync()",
  330. "Found upper missing parent event{}", upper_parent,
  331. );
  332. missing_parents.push(*upper_parent);
  333. }
  334. }
  335. break
  336. }
  337. if !found_event {
  338. error!(
  339. target: "event_graph::dag_sync()",
  340. "[EVENTGRAPH] Sync: Failed to get all events",
  341. );
  342. return Err(Error::DagSyncFailed)
  343. }
  344. }
  345. } // <-- while !missing_parents.is_empty
  346. // At this point we should've got all the events.
  347. // We should add them to the DAG.
  348. // TODO: FIXME: Also validate these events.
  349. for event in received_events.iter().rev() {
  350. self.dag_insert(event.clone()).await.unwrap();
  351. }
  352. info!(target: "event_graph::dag_sync()", "[EVENTGRAPH] DAG synced successfully!");
  353. self.dag_synced.store(true, SeqCst);
  354. Ok(())
  355. }
  356. /// Background task periodically pruning the DAG.
  357. async fn dag_prune(self: Arc<Self>, days_rotation: u64) -> Result<()> {
  358. // The DAG should periodically be pruned. This can be a configurable
  359. // parameter. By pruning, we should deterministically replace the
  360. // genesis event (can use a deterministic timestamp) and drop everything
  361. // in the DAG, leaving just the new genesis event.
  362. debug!(target: "event_graph::dag_prune()", "Spawned background DAG pruning task");
  363. loop {
  364. if days_rotation == 0 {
  365. return Ok(())
  366. }
  367. // Find the next rotation timestamp:
  368. let next_rotation = next_rotation_timestamp(INITIAL_GENESIS, days_rotation);
  369. // Prepare the new genesis event
  370. let current_genesis = Event {
  371. timestamp: next_rotation,
  372. content: GENESIS_CONTENTS.to_vec(),
  373. parents: [NULL_ID; N_EVENT_PARENTS],
  374. };
  375. // Sleep until it's time to rotate.
  376. let s = UNIX_EPOCH.elapsed().unwrap().as_secs() - next_rotation;
  377. debug!(target: "event_graph::dag_prune()", "Sleeping {}s until next DAG prune", s);
  378. sleep(s).await;
  379. debug!(target: "event_graph::dag_prune()", "Rotation period reached. Pruning DAG");
  380. *self.unreferenced_tips.write().await = HashSet::new();
  381. self.dag.clear()?;
  382. self.dag_insert(current_genesis).await?;
  383. debug!(target: "event_graph::dag_prune()", "DAG pruned successfully");
  384. }
  385. }
  386. /// Insert an event into the DAG.
  387. /// This will append the new event into the unreferenced tips set, and
  388. /// remove the event's parents from it. It will also append the event's
  389. /// level-1 parents to the `broadcasted_ids` set, so the P2P protocol
  390. /// knows that any requests for them are actually legitimate.
  391. /// TODO: The `broadcasted_ids` set should periodically be pruned, when
  392. /// some sensible time has passed after broadcasting the event.
  393. pub async fn dag_insert(&self, event: Event) -> Result<blake3::Hash> {
  394. let event_id = event.id();
  395. debug!(target: "event_graph::dag_insert()", "Inserting event {} into the DAG", event_id);
  396. let s_event = serialize_async(&event).await;
  397. // Update the unreferenced DAG tips set
  398. let mut unreferenced_tips = self.unreferenced_tips.write().await;
  399. let mut bcast_ids = self.broadcasted_ids.write().await;
  400. for parent_id in event.parents.iter() {
  401. if parent_id != &NULL_ID {
  402. unreferenced_tips.remove(parent_id);
  403. bcast_ids.insert(*parent_id);
  404. }
  405. }
  406. unreferenced_tips.insert(event_id);
  407. self.dag.insert(event_id.as_bytes(), s_event).unwrap();
  408. // We hold the write locks until this point because we insert the event
  409. // into the database above, so we don't want anything to read these until
  410. // that insertion is complete.
  411. drop(unreferenced_tips);
  412. drop(bcast_ids);
  413. // Notify about the event on the event subscriber
  414. self.event_sub.notify(event).await;
  415. Ok(event_id)
  416. }
  417. /// Fetch an event from the DAG
  418. pub async fn dag_get(&self, event_id: &blake3::Hash) -> Result<Option<Event>> {
  419. let Some(bytes) = self.dag.get(event_id.as_bytes())? else { return Ok(None) };
  420. let event: Event = deserialize_async(&bytes).await?;
  421. Ok(Some(event))
  422. }
  423. /// Find the unreferenced tips in the current DAG state.
  424. async fn find_unreferenced_tips(&self) -> HashSet<blake3::Hash> {
  425. // First get all the event IDs
  426. let mut tips = HashSet::new();
  427. for iter_elem in self.dag.iter() {
  428. let (id, _) = iter_elem.unwrap();
  429. let id = blake3::Hash::from_bytes((&id as &[u8]).try_into().unwrap());
  430. tips.insert(id);
  431. }
  432. for iter_elem in self.dag.iter() {
  433. let (_, event) = iter_elem.unwrap();
  434. let event: Event = deserialize_async(&event).await.unwrap();
  435. for parent in event.parents.iter() {
  436. tips.remove(parent);
  437. }
  438. }
  439. tips
  440. }
  441. /// Get the current set of unreferenced tips in the DAG.
  442. async fn get_unreferenced_tips(&self) -> [blake3::Hash; N_EVENT_PARENTS] {
  443. let unreferenced_tips = self.unreferenced_tips.read().await;
  444. let mut tips = [NULL_ID; N_EVENT_PARENTS];
  445. for (i, tip) in unreferenced_tips.iter().take(N_EVENT_PARENTS).enumerate() {
  446. tips[i] = *tip
  447. }
  448. assert!(tips.iter().any(|x| x != &NULL_ID));
  449. tips
  450. }
  451. /// Internal function used for DAG sorting.
  452. async fn get_unreferenced_tips_sorted(&self) -> [blake3::Hash; N_EVENT_PARENTS] {
  453. let tips = self.get_unreferenced_tips().await;
  454. // Convert the hash to BigUint for sorting
  455. let mut sorted: Vec<_> =
  456. tips.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  457. sorted.sort_unstable();
  458. // Convert back to blake3
  459. let mut tips_sorted = [NULL_ID; N_EVENT_PARENTS];
  460. for (i, id) in sorted.iter().enumerate() {
  461. let mut bytes = id.to_bytes_be();
  462. // Ensure we have 32 bytes
  463. while bytes.len() < blake3::OUT_LEN {
  464. bytes.insert(0, 0);
  465. }
  466. tips_sorted[i] = blake3::Hash::from_bytes(bytes.try_into().unwrap());
  467. }
  468. tips_sorted
  469. }
  470. /// Perform a topological sort of the DAG.
  471. pub async fn order_events(&self) -> Vec<blake3::Hash> {
  472. let mut ordered_events = VecDeque::new();
  473. let mut visited = HashSet::new();
  474. for tip in self.get_unreferenced_tips_sorted().await {
  475. if !visited.contains(&tip) && tip != NULL_ID {
  476. let tip = self.dag.get(tip.as_bytes()).unwrap().unwrap();
  477. let tip = deserialize_async(&tip).await.unwrap();
  478. self.dfs_topological_sort(tip, &mut visited, &mut ordered_events).await;
  479. }
  480. }
  481. ordered_events.make_contiguous().to_vec()
  482. }
  483. /// We do a DFS (<https://en.wikipedia.org/wiki/Depth-first_search>), and
  484. /// additionally we consider the timestamps.
  485. #[async_recursion]
  486. async fn dfs_topological_sort(
  487. &self,
  488. event: Event,
  489. visited: &mut HashSet<blake3::Hash>,
  490. ordered_events: &mut VecDeque<blake3::Hash>,
  491. ) {
  492. let event_id = event.id();
  493. visited.insert(event_id);
  494. for parent_id in event.parents.iter() {
  495. if !visited.contains(parent_id) && parent_id != &NULL_ID {
  496. let p_event = self.dag.get(parent_id.as_bytes()).unwrap().unwrap();
  497. let p_event = deserialize_async(&p_event).await.unwrap();
  498. self.dfs_topological_sort(p_event, visited, ordered_events).await;
  499. }
  500. }
  501. // Before inserting, check timestamps to determine the correct position.
  502. let mut pos = ordered_events.len();
  503. for (idx, existing_id) in ordered_events.iter().enumerate().rev() {
  504. assert!(existing_id != &NULL_ID);
  505. if self.share_same_parents(&event_id, existing_id).await {
  506. let existing_event = self.dag.get(existing_id.as_bytes()).unwrap().unwrap();
  507. let existing_event: Event = deserialize_async(&existing_event).await.unwrap();
  508. // Sort by timestamp
  509. match event.timestamp.cmp(&existing_event.timestamp) {
  510. Ordering::Less => pos = idx,
  511. Ordering::Equal => {
  512. // In case of a tie-breaker, use the event ID
  513. let a = BigUint::from_bytes_be(event_id.as_bytes());
  514. let b = BigUint::from_bytes_be(existing_id.as_bytes());
  515. if a < b {
  516. pos = idx;
  517. }
  518. }
  519. _ => {}
  520. }
  521. }
  522. }
  523. ordered_events.insert(pos, event_id);
  524. }
  525. /// Check if two events have the same parents
  526. async fn share_same_parents(&self, event_id1: &blake3::Hash, event_id2: &blake3::Hash) -> bool {
  527. let event1 = self.dag.get(event_id1.as_bytes()).unwrap().unwrap();
  528. let event1: Event = deserialize_async(&event1).await.unwrap();
  529. let mut parents1: Vec<_> =
  530. event1.parents.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  531. parents1.sort_unstable();
  532. let event2 = self.dag.get(event_id2.as_bytes()).unwrap().unwrap();
  533. let event2: Event = deserialize_async(&event2).await.unwrap();
  534. let mut parents2: Vec<_> =
  535. event2.parents.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  536. parents2.sort_unstable();
  537. parents1 == parents2
  538. }
  539. }