mod.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  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. // First check how many days passed since initial genesis.
  144. let days_passed = days_since(INITIAL_GENESIS);
  145. // Calculate the number of days_rotation intervals since INITIAL_GENESIS
  146. let rotations_since_genesis = days_passed / days_rotation;
  147. // Calculate the timestamp of the most recent event
  148. let timestamp = INITIAL_GENESIS + (rotations_since_genesis * days_rotation * DAY as u64);
  149. Event { timestamp, content: GENESIS_CONTENTS.to_vec(), parents: [NULL_ID; N_EVENT_PARENTS] }
  150. }
  151. /// Sync the DAG from connected peers
  152. pub async fn dag_sync(&self) -> Result<()> {
  153. // We do an optimistic sync where we ask all our connected peers for
  154. // the DAG tips (unreferenced events) and then we accept the ones we
  155. // see the most times.
  156. // * Compare received tips with local ones, identify which we are missing.
  157. // * Request these from peers
  158. // * Recursively request these backward
  159. //
  160. // Verification:
  161. // * Timestamps should go backwards
  162. // * Cross-check with multiple peers, this means we should request the
  163. // same event from multiple peers and make sure it is the same.
  164. // * Since we should be pruning, if we're not synced after some reasonable
  165. // amount of iterations, these could be faulty peers and we can try again
  166. // from the beginning
  167. // Get references to all our peers.
  168. let channels = self.p2p.channels().lock().await.clone();
  169. let mut communicated_peers = channels.len();
  170. info!(
  171. target: "event_graph::dag_sync()",
  172. "[EVENTGRAPH] Syncing DAG from {} peers...", communicated_peers,
  173. );
  174. // Here we keep track of the tips and how many time we've seen them.
  175. let mut tips: HashMap<blake3::Hash, usize> = HashMap::new();
  176. // Let's first ask all of our peers for their tips and collect them
  177. // in our hashmap above.
  178. for (url, channel) in channels.iter() {
  179. let tip_rep_sub = match channel.subscribe_msg::<TipRep>().await {
  180. Ok(v) => v,
  181. Err(e) => {
  182. error!(
  183. target: "event_graph::dag_sync()",
  184. "[EVENTGRAPH] Sync: Couldn't subscribe TipReq for peer {}, skipping ({})",
  185. url, e,
  186. );
  187. communicated_peers -= 1;
  188. continue
  189. }
  190. };
  191. if let Err(e) = channel.send(&TipReq {}).await {
  192. error!(
  193. target: "event_graph::dag_sync()",
  194. "[EVENTGRAPH] Sync: Couldn't contact peer {}, skipping ({})", url, e,
  195. );
  196. communicated_peers -= 1;
  197. continue
  198. };
  199. let peer_tips = match timeout(REPLY_TIMEOUT, tip_rep_sub.receive()).await {
  200. Ok(peer_tips) => peer_tips?,
  201. Err(_) => {
  202. error!(
  203. target: "event_graph::dag_sync()",
  204. "[EVENTGRAPH] Sync: Peer {} didn't reply with tips in time, skipping", url,
  205. );
  206. communicated_peers -= 1;
  207. continue
  208. }
  209. };
  210. let peer_tips = &peer_tips.0;
  211. // Note down the seen tips
  212. for tip in peer_tips {
  213. if let Some(seen_tip) = tips.get_mut(tip) {
  214. *seen_tip += 1;
  215. } else {
  216. tips.insert(*tip, 1);
  217. }
  218. }
  219. }
  220. // After we've communicated all the peers, let's see what happened.
  221. if tips.is_empty() {
  222. error!(
  223. target: "event_graph::dag_sync()",
  224. "[EVENTGRAPH] Sync: Could not find any DAG tips",
  225. );
  226. return Err(Error::DagSyncFailed)
  227. }
  228. // We know the number of peers we've communicated with.
  229. // Arbitrarily, let's not consider events we only got once.
  230. // TODO: This should be more sensible depending on the peer number.
  231. let mut considered_tips = HashSet::new();
  232. for (tip, amount) in tips.iter() {
  233. if amount > &1 {
  234. considered_tips.insert(*tip);
  235. }
  236. }
  237. drop(tips);
  238. // Now begin fetching the events backwards.
  239. let mut missing_parents = vec![];
  240. for tip in considered_tips.iter() {
  241. assert!(tip != &NULL_ID);
  242. if !self.dag.contains_key(tip.as_bytes()).unwrap() {
  243. missing_parents.push(*tip);
  244. }
  245. }
  246. if missing_parents.is_empty() {
  247. return Ok(())
  248. }
  249. info!(target: "event_graph::dag_sync()", "[EVENTGRAPH] Fetching events");
  250. let mut received_events = vec![];
  251. while !missing_parents.is_empty() {
  252. for parent_id in missing_parents.clone().iter() {
  253. let mut found_event = false;
  254. for (url, channel) in channels.iter() {
  255. debug!(
  256. target: "event_graph::dag_sync()",
  257. "Requesting {} from {}...", parent_id, url,
  258. );
  259. let ev_rep_sub = match channel.subscribe_msg::<EventRep>().await {
  260. Ok(v) => v,
  261. Err(e) => {
  262. error!(
  263. target: "event_graph::dag_sync()",
  264. "[EVENTGRAPH] Sync: Couldn't subscribe EventRep for peer {}, skipping ({})",
  265. url, e,
  266. );
  267. continue
  268. }
  269. };
  270. if let Err(e) = channel.send(&EventReq(*parent_id)).await {
  271. error!(
  272. target: "event_graph::dag_sync()",
  273. "[EVENTGRAPH] Sync: Failed communicating EventReq({}) to {}: {}",
  274. parent_id, url, e,
  275. );
  276. continue
  277. }
  278. let parent = match timeout(REPLY_TIMEOUT, ev_rep_sub.receive()).await {
  279. Ok(parent) => parent,
  280. Err(_) => {
  281. error!(
  282. target: "event_graph::dag_sync()",
  283. "[EVENTGRAPH] Sync: Timeout waiting for parent {} from {}",
  284. parent_id, url,
  285. );
  286. continue
  287. }
  288. };
  289. let parent = match parent {
  290. Ok(v) => v.0.clone(),
  291. Err(e) => {
  292. error!(
  293. target: "event_graph::dag_sync()",
  294. "[EVENTGRAPH] Sync: Failed receiving parent {}: {}",
  295. parent_id, e,
  296. );
  297. continue
  298. }
  299. };
  300. if &parent.id() != parent_id {
  301. error!(
  302. target: "event_graph::dag_sync()",
  303. "[EVENTGRAPH] Sync: Peer {} replied with a wrong event: {}",
  304. url, parent.id(),
  305. );
  306. continue
  307. }
  308. debug!(
  309. target: "event_graph::dag_sync()",
  310. "Got correct parent event {}", parent_id,
  311. );
  312. received_events.push(parent.clone());
  313. let pos = missing_parents.iter().position(|id| id == &parent.id()).unwrap();
  314. missing_parents.remove(pos);
  315. found_event = true;
  316. // See if we have the upper parents
  317. for upper_parent in parent.parents.iter() {
  318. if upper_parent == &NULL_ID {
  319. continue
  320. }
  321. if !self.dag.contains_key(upper_parent.as_bytes()).unwrap() {
  322. debug!(
  323. target: "event_graph::dag_sync()",
  324. "Found upper missing parent event{}", upper_parent,
  325. );
  326. missing_parents.push(*upper_parent);
  327. }
  328. }
  329. break
  330. }
  331. if !found_event {
  332. error!(
  333. target: "event_graph::dag_sync()",
  334. "[EVENTGRAPH] Sync: Failed to get all events",
  335. );
  336. return Err(Error::DagSyncFailed)
  337. }
  338. }
  339. } // <-- while !missing_parents.is_empty
  340. // At this point we should've got all the events.
  341. // We should add them to the DAG.
  342. // TODO: FIXME: Also validate these events.
  343. for event in received_events.iter().rev() {
  344. self.dag_insert(event.clone()).await.unwrap();
  345. }
  346. info!(target: "event_graph::dag_sync()", "[EVENTGRAPH] DAG synced successfully!");
  347. self.dag_synced.store(true, SeqCst);
  348. Ok(())
  349. }
  350. /// Background task periodically pruning the DAG.
  351. async fn dag_prune(self: Arc<Self>, days_rotation: u64) -> Result<()> {
  352. // The DAG should periodically be pruned. This can be a configurable
  353. // parameter. By pruning, we should deterministically replace the
  354. // genesis event (can use a deterministic timestamp) and drop everything
  355. // in the DAG, leaving just the new genesis event.
  356. debug!(target: "event_graph::dag_prune()", "Spawned background DAG pruning task");
  357. loop {
  358. // Find the next rotation timestamp:
  359. let next_rotation = next_rotation_timestamp(INITIAL_GENESIS, days_rotation);
  360. // Prepare the new genesis event
  361. let current_genesis = Event {
  362. timestamp: next_rotation,
  363. content: GENESIS_CONTENTS.to_vec(),
  364. parents: [NULL_ID; N_EVENT_PARENTS],
  365. };
  366. // Sleep until it's time to rotate.
  367. let s = UNIX_EPOCH.elapsed().unwrap().as_secs() - next_rotation;
  368. debug!(target: "event_graph::dag_prune()", "Sleeping {}s until next DAG prune", s);
  369. sleep(s).await;
  370. debug!(target: "event_graph::dag_prune()", "Rotation period reached. Pruning DAG");
  371. *self.unreferenced_tips.write().await = HashSet::new();
  372. self.dag.clear()?;
  373. self.dag_insert(current_genesis).await?;
  374. debug!(target: "event_graph::dag_prune()", "DAG pruned successfully");
  375. }
  376. }
  377. /// Insert an event into the DAG.
  378. /// This will append the new event into the unreferenced tips set, and
  379. /// remove the event's parents from it. It will also append the event's
  380. /// level-1 parents to the `broadcasted_ids` set, so the P2P protocol
  381. /// knows that any requests for them are actually legitimate.
  382. /// TODO: The `broadcasted_ids` set should periodically be pruned, when
  383. /// some sensible time has passed after broadcasting the event.
  384. pub async fn dag_insert(&self, event: Event) -> Result<blake3::Hash> {
  385. let event_id = event.id();
  386. debug!(target: "event_graph::dag_insert()", "Inserting event {} into the DAG", event_id);
  387. let s_event = serialize_async(&event).await;
  388. // Update the unreferenced DAG tips set
  389. let mut unreferenced_tips = self.unreferenced_tips.write().await;
  390. let mut bcast_ids = self.broadcasted_ids.write().await;
  391. for parent_id in event.parents.iter() {
  392. if parent_id != &NULL_ID {
  393. unreferenced_tips.remove(parent_id);
  394. bcast_ids.insert(*parent_id);
  395. }
  396. }
  397. unreferenced_tips.insert(event_id);
  398. self.dag.insert(event_id.as_bytes(), s_event).unwrap();
  399. // We hold the write locks until this point because we insert the event
  400. // into the database above, so we don't want anything to read these until
  401. // that insertion is complete.
  402. drop(unreferenced_tips);
  403. drop(bcast_ids);
  404. // Notify about the event on the event subscriber
  405. self.event_sub.notify(event).await;
  406. Ok(event_id)
  407. }
  408. /// Fetch an event from the DAG
  409. pub async fn dag_get(&self, event_id: &blake3::Hash) -> Result<Option<Event>> {
  410. let Some(bytes) = self.dag.get(event_id.as_bytes())? else { return Ok(None) };
  411. let event: Event = deserialize_async(&bytes).await?;
  412. Ok(Some(event))
  413. }
  414. /// Find the unreferenced tips in the current DAG state.
  415. async fn find_unreferenced_tips(&self) -> HashSet<blake3::Hash> {
  416. // First get all the event IDs
  417. let mut tips = HashSet::new();
  418. for iter_elem in self.dag.iter() {
  419. let (id, _) = iter_elem.unwrap();
  420. let id = blake3::Hash::from_bytes((&id as &[u8]).try_into().unwrap());
  421. tips.insert(id);
  422. }
  423. for iter_elem in self.dag.iter() {
  424. let (_, event) = iter_elem.unwrap();
  425. let event: Event = deserialize_async(&event).await.unwrap();
  426. for parent in event.parents.iter() {
  427. tips.remove(parent);
  428. }
  429. }
  430. tips
  431. }
  432. /// Get the current set of unreferenced tips in the DAG.
  433. async fn get_unreferenced_tips(&self) -> [blake3::Hash; N_EVENT_PARENTS] {
  434. let unreferenced_tips = self.unreferenced_tips.read().await;
  435. let mut tips = [NULL_ID; N_EVENT_PARENTS];
  436. for (i, tip) in unreferenced_tips.iter().take(N_EVENT_PARENTS).enumerate() {
  437. tips[i] = *tip
  438. }
  439. assert!(tips.iter().any(|x| x != &NULL_ID));
  440. tips
  441. }
  442. /// Internal function used for DAG sorting.
  443. async fn get_unreferenced_tips_sorted(&self) -> [blake3::Hash; N_EVENT_PARENTS] {
  444. let tips = self.get_unreferenced_tips().await;
  445. // Convert the hash to BigUint for sorting
  446. let mut sorted: Vec<_> =
  447. tips.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  448. sorted.sort_unstable();
  449. // Convert back to blake3
  450. let mut tips_sorted = [NULL_ID; N_EVENT_PARENTS];
  451. for (i, id) in sorted.iter().enumerate() {
  452. let mut bytes = id.to_bytes_be();
  453. // Ensure we have 32 bytes
  454. while bytes.len() < blake3::OUT_LEN {
  455. bytes.insert(0, 0);
  456. }
  457. tips_sorted[i] = blake3::Hash::from_bytes(bytes.try_into().unwrap());
  458. }
  459. tips_sorted
  460. }
  461. /// Perform a topological sort of the DAG.
  462. pub async fn order_events(&self) -> Vec<blake3::Hash> {
  463. let mut ordered_events = VecDeque::new();
  464. let mut visited = HashSet::new();
  465. for tip in self.get_unreferenced_tips_sorted().await {
  466. if !visited.contains(&tip) && tip != NULL_ID {
  467. let tip = self.dag.get(tip.as_bytes()).unwrap().unwrap();
  468. let tip = deserialize_async(&tip).await.unwrap();
  469. self.dfs_topological_sort(tip, &mut visited, &mut ordered_events).await;
  470. }
  471. }
  472. ordered_events.make_contiguous().to_vec()
  473. }
  474. /// We do a DFS (<https://en.wikipedia.org/wiki/Depth-first_search>), and
  475. /// additionally we consider the timestamps.
  476. #[async_recursion]
  477. async fn dfs_topological_sort(
  478. &self,
  479. event: Event,
  480. visited: &mut HashSet<blake3::Hash>,
  481. ordered_events: &mut VecDeque<blake3::Hash>,
  482. ) {
  483. let event_id = event.id();
  484. visited.insert(event_id);
  485. for parent_id in event.parents.iter() {
  486. if !visited.contains(parent_id) && parent_id != &NULL_ID {
  487. let p_event = self.dag.get(parent_id.as_bytes()).unwrap().unwrap();
  488. let p_event = deserialize_async(&p_event).await.unwrap();
  489. self.dfs_topological_sort(p_event, visited, ordered_events).await;
  490. }
  491. }
  492. // Before inserting, check timestamps to determine the correct position.
  493. let mut pos = ordered_events.len();
  494. for (idx, existing_id) in ordered_events.iter().enumerate().rev() {
  495. assert!(existing_id != &NULL_ID);
  496. if self.share_same_parents(&event_id, existing_id).await {
  497. let existing_event = self.dag.get(existing_id.as_bytes()).unwrap().unwrap();
  498. let existing_event: Event = deserialize_async(&existing_event).await.unwrap();
  499. // Sort by timestamp
  500. match event.timestamp.cmp(&existing_event.timestamp) {
  501. Ordering::Less => pos = idx,
  502. Ordering::Equal => {
  503. // In case of a tie-breaker, use the event ID
  504. let a = BigUint::from_bytes_be(event_id.as_bytes());
  505. let b = BigUint::from_bytes_be(existing_id.as_bytes());
  506. if a < b {
  507. pos = idx;
  508. }
  509. }
  510. _ => {}
  511. }
  512. }
  513. }
  514. ordered_events.insert(pos, event_id);
  515. }
  516. /// Check if two events have the same parents
  517. async fn share_same_parents(&self, event_id1: &blake3::Hash, event_id2: &blake3::Hash) -> bool {
  518. let event1 = self.dag.get(event_id1.as_bytes()).unwrap().unwrap();
  519. let event1: Event = deserialize_async(&event1).await.unwrap();
  520. let mut parents1: Vec<_> =
  521. event1.parents.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  522. parents1.sort_unstable();
  523. let event2 = self.dag.get(event_id2.as_bytes()).unwrap().unwrap();
  524. let event2: Event = deserialize_async(&event2).await.unwrap();
  525. let mut parents2: Vec<_> =
  526. event2.parents.iter().map(|x| BigUint::from_bytes_be(x.as_bytes())).collect();
  527. parents2.sort_unstable();
  528. parents1 == parents2
  529. }
  530. }