mod.rs 11 KB

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