mod.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. use std::collections::{HashMap, HashSet};
  2. use async_std::sync::{Arc, RwLock};
  3. use chrono::Utc;
  4. use darkfi_serial::serialize;
  5. use futures::{select, FutureExt};
  6. use log::{debug, error, warn};
  7. use rand::Rng;
  8. use smol::Executor;
  9. use crate::{
  10. net,
  11. net::P2pPtr,
  12. util::async_util::sleep,
  13. Error::{NetworkNotConnected, UnknownKey},
  14. Result,
  15. };
  16. mod messages;
  17. use messages::{KeyRequest, KeyResponse, LookupMapRequest, LookupMapResponse, LookupRequest};
  18. mod protocol;
  19. use protocol::Protocol;
  20. // Constants configuration
  21. const SEEN_DURATION: i64 = 120;
  22. /// Atomic pointer to DHT state
  23. pub type DhtPtr = Arc<RwLock<Dht>>;
  24. // TODO: proper errors
  25. // TODO: lookup table to be based on directly connected peers, not broadcast based
  26. // Using string in structures because we are at an external crate
  27. // and cant use blake3 serialization. To be replaced once merged with core src.
  28. /// Struct representing DHT state.
  29. pub struct Dht {
  30. /// Daemon id
  31. pub id: blake3::Hash,
  32. /// Daemon hasmap
  33. pub map: HashMap<blake3::Hash, Vec<u8>>,
  34. /// Network lookup map, containing nodes that holds each key
  35. pub lookup: HashMap<blake3::Hash, HashSet<blake3::Hash>>,
  36. /// P2P network pointer
  37. pub p2p: P2pPtr,
  38. /// Channel to receive responses from P2P
  39. p2p_recv_channel: smol::channel::Receiver<KeyResponse>,
  40. /// Stop signal channel to terminate background processes
  41. stop_signal: smol::channel::Receiver<()>,
  42. /// Daemon seen requests/responses ids and timestamp,
  43. /// to prevent rebroadcasting and loops
  44. pub seen: HashMap<blake3::Hash, i64>,
  45. }
  46. impl Dht {
  47. pub async fn new(
  48. initial: Option<HashMap<blake3::Hash, HashSet<blake3::Hash>>>,
  49. p2p_ptr: P2pPtr,
  50. stop_signal: smol::channel::Receiver<()>,
  51. ex: Arc<Executor<'_>>,
  52. ) -> Result<DhtPtr> {
  53. // Generate a random id
  54. let mut rng = rand::thread_rng();
  55. let n: u16 = rng.gen();
  56. let id = blake3::hash(&serialize(&n));
  57. let map = HashMap::default();
  58. let lookup = match initial {
  59. Some(l) => l,
  60. None => HashMap::default(),
  61. };
  62. let p2p = p2p_ptr.clone();
  63. let (p2p_send_channel, p2p_recv_channel) = smol::channel::unbounded::<KeyResponse>();
  64. let seen = HashMap::default();
  65. let dht = Arc::new(RwLock::new(Dht {
  66. id,
  67. map,
  68. lookup,
  69. p2p,
  70. p2p_recv_channel,
  71. stop_signal,
  72. seen,
  73. }));
  74. // Registering P2P protocols
  75. let registry = p2p_ptr.protocol_registry();
  76. let _dht = dht.clone();
  77. registry
  78. .register(net::SESSION_ALL, move |channel, p2p_ptr| {
  79. let sender = p2p_send_channel.clone();
  80. let dht = _dht.clone();
  81. async move { Protocol::init(channel, sender, dht, p2p_ptr).await.unwrap() }
  82. })
  83. .await;
  84. // Task to periodically clean up daemon seen messages
  85. ex.spawn(prune_seen_messages(dht.clone())).detach();
  86. Ok(dht)
  87. }
  88. /// Store provided key value pair, update lookup map and broadcast new insert to network
  89. pub async fn insert(
  90. &mut self,
  91. key: blake3::Hash,
  92. value: Vec<u8>,
  93. ) -> Result<Option<blake3::Hash>> {
  94. self.map.insert(key, value);
  95. if let Err(e) = self.lookup_insert(key, self.id) {
  96. error!("Failed to insert record to lookup map: {}", e);
  97. return Err(e)
  98. };
  99. let request = LookupRequest::new(self.id, key, 0);
  100. if let Err(e) = self.p2p.broadcast(request).await {
  101. error!("Failed broadcasting request: {}", e);
  102. return Err(e)
  103. }
  104. Ok(Some(key))
  105. }
  106. /// Remove provided key value pair and update lookup map
  107. pub async fn remove(&mut self, key: blake3::Hash) -> Result<Option<blake3::Hash>> {
  108. // Check if key value pair existed and act accordingly
  109. match self.map.remove(&key) {
  110. Some(_) => {
  111. debug!("Key removed: {}", key);
  112. let request = LookupRequest::new(self.id, key, 1);
  113. if let Err(e) = self.p2p.broadcast(request).await {
  114. error!("Failed broadcasting request: {}", e);
  115. return Err(e)
  116. }
  117. self.lookup_remove(key, self.id)
  118. }
  119. None => Ok(None),
  120. }
  121. }
  122. /// Store provided key node pair in lookup map and update network
  123. pub fn lookup_insert(
  124. &mut self,
  125. key: blake3::Hash,
  126. node_id: blake3::Hash,
  127. ) -> Result<Option<blake3::Hash>> {
  128. let mut lookup_set = match self.lookup.get(&key) {
  129. Some(s) => s.clone(),
  130. None => HashSet::new(),
  131. };
  132. lookup_set.insert(node_id);
  133. self.lookup.insert(key, lookup_set);
  134. Ok(Some(key))
  135. }
  136. /// Remove provided node id from keys set in local lookup map
  137. pub fn lookup_remove(
  138. &mut self,
  139. key: blake3::Hash,
  140. node_id: blake3::Hash,
  141. ) -> Result<Option<blake3::Hash>> {
  142. if let Some(s) = self.lookup.get(&key) {
  143. let mut lookup_set = s.clone();
  144. lookup_set.remove(&node_id);
  145. if lookup_set.is_empty() {
  146. self.lookup.remove(&key);
  147. } else {
  148. self.lookup.insert(key, lookup_set);
  149. }
  150. }
  151. Ok(Some(key))
  152. }
  153. /// Verify if provided key exists and return flag if local or in network
  154. pub fn contains_key(&self, key: blake3::Hash) -> Option<bool> {
  155. match self.lookup.contains_key(&key) {
  156. true => Some(self.map.contains_key(&key)),
  157. false => None,
  158. }
  159. }
  160. /// Get key from local map, acting as daemon cache
  161. pub fn get(&self, key: blake3::Hash) -> Option<&Vec<u8>> {
  162. self.map.get(&key)
  163. }
  164. /// Generate key request and broadcast it to the network
  165. pub async fn request_key(&self, key: blake3::Hash) -> Result<()> {
  166. // Verify the key exist in the lookup map.
  167. let peers = match self.lookup.get(&key) {
  168. Some(v) => v.clone(),
  169. None => return Err(UnknownKey),
  170. };
  171. debug!("Key is in peers: {:?}", peers);
  172. // We retrieve p2p network connected channels, to verify if we
  173. // are connected to a network.
  174. // Using len here because is_empty() uses unstable library feature
  175. // called 'exact_size_is_empty'.
  176. if self.p2p.channels().lock().await.values().len() == 0 {
  177. return Err(NetworkNotConnected)
  178. }
  179. // We create a key request, and broadcast it to the network
  180. // We choose last known peer as request recipient
  181. let peer = *peers.iter().last().unwrap();
  182. let request = KeyRequest::new(self.id, peer, key);
  183. // TODO: ask connected peers directly, not broadcast
  184. if let Err(e) = self.p2p.broadcast(request).await {
  185. error!("Failed broadcasting request: {}", e);
  186. return Err(e)
  187. }
  188. Ok(())
  189. }
  190. /// Auxilary function to sync lookup map with network
  191. pub async fn sync_lookup_map(&mut self) -> Result<()> {
  192. debug!("Starting lookup map sync...");
  193. let channels_map = self.p2p.channels().lock().await.clone();
  194. let values = channels_map.values();
  195. // Using len here because is_empty() uses unstable library feature
  196. // called 'exact_size_is_empty'.
  197. if values.len() != 0 {
  198. // Node iterates the channel peers to ask for their lookup map
  199. for channel in values {
  200. // Communication setup
  201. let msg_subsystem = channel.get_message_subsystem();
  202. msg_subsystem.add_dispatch::<LookupMapResponse>().await;
  203. let response_sub = channel.subscribe_msg::<LookupMapResponse>().await?;
  204. // Node creates a `LookupMapRequest` and sends it
  205. let order = LookupMapRequest::new(self.id);
  206. channel.send(order).await?;
  207. // Node stores response data.
  208. let resp = response_sub.receive().await?;
  209. if resp.lookup.is_empty() {
  210. warn!("Retrieved empty lookup map from an unsynced node, retrying...");
  211. continue
  212. }
  213. // Store retrieved records
  214. debug!("Processing received records");
  215. for (k, v) in &resp.lookup {
  216. for node in v {
  217. self.lookup_insert(*k, *node)?;
  218. }
  219. }
  220. break
  221. }
  222. } else {
  223. warn!("Node is not connected to other nodes");
  224. }
  225. debug!("Lookup map synced!");
  226. Ok(())
  227. }
  228. }
  229. // Auxilary function to wait for a key response from the P2P network.
  230. pub async fn waiting_for_response(dht: DhtPtr) -> Result<Option<KeyResponse>> {
  231. let (p2p_recv_channel, stop_signal, timeout) = {
  232. let _dht = dht.read().await;
  233. (
  234. _dht.p2p_recv_channel.clone(),
  235. _dht.stop_signal.clone(),
  236. _dht.p2p.settings().connect_timeout_seconds as u64,
  237. )
  238. };
  239. let ex = Arc::new(Executor::new());
  240. let (timeout_s, timeout_r) = smol::channel::unbounded::<()>();
  241. ex.spawn(async move {
  242. sleep(timeout).await;
  243. timeout_s.send(()).await.unwrap_or(());
  244. })
  245. .detach();
  246. select! {
  247. msg = p2p_recv_channel.recv().fuse() => {
  248. let response = msg?;
  249. return Ok(Some(response))
  250. },
  251. _ = stop_signal.recv().fuse() => {},
  252. _ = timeout_r.recv().fuse() => {},
  253. }
  254. Ok(None)
  255. }
  256. // Auxilary function to periodically prun seen messages, based on when they were received.
  257. // This helps us to prevent broadcasting loops.
  258. async fn prune_seen_messages(dht: DhtPtr) {
  259. loop {
  260. sleep(SEEN_DURATION as u64).await;
  261. debug!("Pruning seen messages");
  262. let now = Utc::now().timestamp();
  263. let mut prune = vec![];
  264. let map = dht.read().await.seen.clone();
  265. for (k, v) in map.iter() {
  266. if now - v > SEEN_DURATION {
  267. prune.push(k);
  268. }
  269. }
  270. let mut map = map.clone();
  271. for i in prune {
  272. map.remove(i);
  273. }
  274. dht.write().await.seen = map;
  275. }
  276. }