dht.rs 11 KB

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