main.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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 async_std::{
  19. stream::StreamExt,
  20. sync::{Arc, Mutex},
  21. };
  22. use libc::mkfifo;
  23. use std::{
  24. collections::HashMap,
  25. env,
  26. ffi::CString,
  27. fs::{create_dir_all, remove_dir_all},
  28. io::{stdin, Write},
  29. path::Path,
  30. };
  31. use crypto_box::{
  32. aead::{Aead, AeadCore},
  33. ChaChaBox, SecretKey,
  34. };
  35. use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
  36. use futures::{select, FutureExt};
  37. use log::{debug, error, info};
  38. use rand::rngs::OsRng;
  39. use structopt_toml::StructOptToml;
  40. use darkfi::{
  41. async_daemonize,
  42. event_graph::{
  43. events_queue::EventsQueue,
  44. model::{Event, EventId, Model, ModelPtr},
  45. protocol_event::{ProtocolEvent, Seen, SeenPtr},
  46. view::{View, ViewPtr},
  47. EventMsg,
  48. },
  49. net::{self, P2pPtr},
  50. rpc::server::listen_and_serve,
  51. util::{path::expand_path, time::Timestamp},
  52. Error, Result,
  53. };
  54. mod error;
  55. mod jsonrpc;
  56. mod month_tasks;
  57. mod settings;
  58. mod task_info;
  59. mod util;
  60. use crate::{
  61. error::{TaudError, TaudResult},
  62. jsonrpc::JsonRpcInterface,
  63. settings::{Args, CONFIG_FILE, CONFIG_FILE_CONTENTS},
  64. task_info::{TaskEvent, TaskInfo},
  65. util::pipe_write,
  66. };
  67. fn get_workspaces(settings: &Args) -> Result<HashMap<String, ChaChaBox>> {
  68. let mut workspaces = HashMap::new();
  69. for workspace in settings.workspaces.iter() {
  70. let workspace: Vec<&str> = workspace.split(':').collect();
  71. let (workspace, secret) = (workspace[0], workspace[1]);
  72. let bytes: [u8; 32] = bs58::decode(secret)
  73. .into_vec()?
  74. .try_into()
  75. .map_err(|_| Error::ParseFailed("Parse secret key failed"))?;
  76. let secret = crypto_box::SecretKey::from(bytes);
  77. let public = secret.public_key();
  78. let chacha_box = crypto_box::ChaChaBox::new(&public, &secret);
  79. workspaces.insert(workspace.to_string(), chacha_box);
  80. }
  81. Ok(workspaces)
  82. }
  83. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  84. pub struct EncryptedTask {
  85. payload: String,
  86. }
  87. impl EventMsg for EncryptedTask {
  88. fn new() -> Self {
  89. Self { payload: String::from("root") }
  90. }
  91. }
  92. fn encrypt_task(
  93. task: &TaskInfo,
  94. chacha_box: &ChaChaBox,
  95. rng: &mut OsRng,
  96. ) -> TaudResult<EncryptedTask> {
  97. debug!("start encrypting task");
  98. let nonce = ChaChaBox::generate_nonce(rng);
  99. let payload = &serialize(task)[..];
  100. let mut payload = chacha_box.encrypt(&nonce, payload)?;
  101. let mut concat = vec![];
  102. concat.append(&mut nonce.as_slice().to_vec());
  103. concat.append(&mut payload);
  104. let payload = bs58::encode(concat.clone()).into_string();
  105. Ok(EncryptedTask { payload })
  106. }
  107. fn try_decrypt_task(encrypt_task: &EncryptedTask, chacha_box: &ChaChaBox) -> TaudResult<TaskInfo> {
  108. debug!("start decrypting task");
  109. let bytes = match bs58::decode(&encrypt_task.payload).into_vec() {
  110. Ok(v) => v,
  111. Err(_) => return Err(TaudError::DecryptionError("Error decoding payload".to_string())),
  112. };
  113. if bytes.len() < 25 {
  114. return Err(TaudError::DecryptionError("Invalid bytes length".to_string()))
  115. }
  116. // Try extracting the nonce
  117. let nonce = match bytes[0..24].try_into() {
  118. Ok(v) => v,
  119. Err(_) => return Err(TaudError::DecryptionError("Invalid nonce".to_string())),
  120. };
  121. // Take the remaining ciphertext
  122. let message = &bytes[24..];
  123. // let nonce = encrypt_task.nonce.as_slice();
  124. let decrypted_task = chacha_box.decrypt(nonce, message)?;
  125. let task = deserialize(&decrypted_task)?;
  126. Ok(task)
  127. }
  128. #[allow(clippy::too_many_arguments)]
  129. async fn start_sync_loop(
  130. broadcast_rcv: smol::channel::Receiver<TaskInfo>,
  131. view: ViewPtr<EncryptedTask>,
  132. model: ModelPtr<EncryptedTask>,
  133. seen: SeenPtr<EventId>,
  134. workspaces: Arc<HashMap<String, ChaChaBox>>,
  135. datastore_path: std::path::PathBuf,
  136. missed_events: Arc<Mutex<Vec<Event<EncryptedTask>>>>,
  137. piped: bool,
  138. p2p: P2pPtr,
  139. ) -> TaudResult<()> {
  140. loop {
  141. let mut v = view.lock().await;
  142. select! {
  143. task_event = broadcast_rcv.recv().fuse() => {
  144. let tk = task_event.map_err(Error::from)?;
  145. if workspaces.contains_key(&tk.workspace) {
  146. let chacha_box = workspaces.get(&tk.workspace).unwrap();
  147. let encrypted_task = encrypt_task(&tk, chacha_box, &mut OsRng)?;
  148. info!(target: "tau", "Send the task: ref: {}", tk.ref_id);
  149. let event = Event {
  150. previous_event_hash: model.lock().await.get_head_hash(),
  151. action: encrypted_task,
  152. timestamp: Timestamp::current_time(),
  153. };
  154. p2p.broadcast(&event).await;
  155. }
  156. }
  157. task_event = v.process().fuse() => {
  158. let event = task_event.map_err(Error::from)?;
  159. if !seen.push(&event.hash()).await {
  160. continue
  161. }
  162. missed_events.lock().await.push(event.clone());
  163. on_receive_task(&event.action, &datastore_path, &workspaces, piped)
  164. .await?;
  165. }
  166. }
  167. }
  168. }
  169. async fn on_receive_task(
  170. task: &EncryptedTask,
  171. datastore_path: &Path,
  172. workspaces: &HashMap<String, ChaChaBox>,
  173. piped: bool,
  174. ) -> TaudResult<()> {
  175. for (workspace, chacha_box) in workspaces.iter() {
  176. let task = try_decrypt_task(task, chacha_box);
  177. if let Err(e) = task {
  178. debug!("unable to decrypt the task: {}", e);
  179. continue
  180. }
  181. let mut task = task.unwrap();
  182. info!(target: "tau", "Save the task: ref: {}", task.ref_id);
  183. task.workspace = workspace.clone();
  184. if piped {
  185. // if we can't load the task then it's a new task.
  186. // otherwise it's a modification.
  187. match TaskInfo::load(&task.ref_id, datastore_path) {
  188. Ok(loaded_task) => {
  189. let loaded_events = loaded_task.events.0;
  190. let mut events = task.events.0.clone();
  191. events.retain(|ev| !loaded_events.contains(ev));
  192. let file = "/tmp/tau_pipe";
  193. let mut pipe_write = pipe_write(file)?;
  194. let mut task_clone = task.clone();
  195. task_clone.events.0 = events;
  196. let json = serde_json::to_string(&task_clone).unwrap();
  197. pipe_write.write_all(json.as_bytes())?;
  198. }
  199. Err(_) => {
  200. let file = "/tmp/tau_pipe";
  201. let mut pipe_write = pipe_write(file)?;
  202. let mut task_clone = task.clone();
  203. task_clone.events.0.push(TaskEvent::new(
  204. "add_task".to_string(),
  205. task_clone.owner.clone(),
  206. "".to_string(),
  207. ));
  208. let json = serde_json::to_string(&task_clone).unwrap();
  209. pipe_write.write_all(json.as_bytes())?;
  210. }
  211. }
  212. }
  213. task.save(datastore_path)?;
  214. }
  215. Ok(())
  216. }
  217. async_daemonize!(realmain);
  218. async fn realmain(settings: Args, executor: Arc<smol::Executor<'_>>) -> Result<()> {
  219. let datastore_path = expand_path(&settings.datastore)?;
  220. let nickname =
  221. if settings.nickname.is_some() { settings.nickname.clone() } else { env::var("USER").ok() };
  222. if settings.refresh {
  223. println!("Removing local data in: {:?} (yes/no)? ", datastore_path);
  224. let mut confirm = String::new();
  225. stdin().read_line(&mut confirm).expect("Failed to read line");
  226. let confirm = confirm.to_lowercase();
  227. let confirm = confirm.trim();
  228. if confirm == "yes" || confirm == "y" {
  229. remove_dir_all(datastore_path).unwrap_or(());
  230. println!("Local data removed successfully.");
  231. } else {
  232. error!("Unexpected Value: {}", confirm);
  233. }
  234. return Ok(())
  235. }
  236. if nickname.is_none() {
  237. error!("Provide a nickname in config file");
  238. return Ok(())
  239. }
  240. if settings.piped {
  241. let file = "/tmp/tau_pipe";
  242. let path = CString::new(file).unwrap();
  243. unsafe { mkfifo(path.as_ptr(), 0o644) };
  244. }
  245. // mkdir datastore_path if not exists
  246. create_dir_all(datastore_path.clone())?;
  247. create_dir_all(datastore_path.join("month"))?;
  248. create_dir_all(datastore_path.join("task"))?;
  249. if settings.generate {
  250. println!("Generating a new workspace");
  251. loop {
  252. println!("Name for the new workspace: ");
  253. let mut workspace = String::new();
  254. stdin().read_line(&mut workspace).expect("Failed to read line");
  255. let workspace = workspace.to_lowercase();
  256. let workspace = workspace.trim();
  257. if workspace.is_empty() && workspace.len() < 3 {
  258. error!("Wrong workspace try again");
  259. continue
  260. }
  261. let secret_key = SecretKey::generate(&mut OsRng);
  262. let encoded = bs58::encode(secret_key.to_bytes());
  263. println!("workspace: {}:{}", workspace, encoded.into_string());
  264. println!("Please add it to the config file.");
  265. break
  266. }
  267. return Ok(())
  268. }
  269. let workspaces = Arc::new(get_workspaces(&settings)?);
  270. if workspaces.is_empty() {
  271. error!("Please add at least one workspace to the config file.");
  272. println!("Run `$ taud --generate` to generate new workspace.");
  273. return Ok(())
  274. }
  275. ////////////////////
  276. // Initialize the base structures
  277. ////////////////////
  278. let events_queue = EventsQueue::<EncryptedTask>::new();
  279. let model = Arc::new(Mutex::new(Model::new(events_queue.clone())));
  280. let view = Arc::new(Mutex::new(View::new(events_queue)));
  281. let model_clone = model.clone();
  282. model.lock().await.load_tree(&datastore_path)?;
  283. ////////////////////
  284. // Buffers
  285. ////////////////////
  286. let seen_event = Seen::new();
  287. let seen_inv = Seen::new();
  288. let (broadcast_snd, broadcast_rcv) = smol::channel::unbounded::<TaskInfo>();
  289. //
  290. // P2p setup
  291. //
  292. let net_settings = settings.net.clone();
  293. let p2p = net::P2p::new(net_settings.into()).await;
  294. let registry = p2p.protocol_registry();
  295. registry
  296. .register(net::SESSION_ALL, move |channel, p2p| {
  297. let seen_event = seen_event.clone();
  298. let seen_inv = seen_inv.clone();
  299. let model = model.clone();
  300. async move { ProtocolEvent::init(channel, p2p, model, seen_event, seen_inv).await }
  301. })
  302. .await;
  303. p2p.clone().start(executor.clone()).await?;
  304. executor.spawn(p2p.clone().run(executor.clone())).detach();
  305. ////////////////////
  306. // Listner
  307. ////////////////////
  308. let seen_ids = Seen::new();
  309. let missed_events = Arc::new(Mutex::new(vec![]));
  310. executor
  311. .spawn(start_sync_loop(
  312. broadcast_rcv,
  313. view,
  314. model_clone.clone(),
  315. seen_ids,
  316. workspaces.clone(),
  317. datastore_path.clone(),
  318. missed_events,
  319. settings.piped,
  320. p2p.clone(),
  321. ))
  322. .detach();
  323. //
  324. // RPC interface
  325. //
  326. let rpc_interface = Arc::new(JsonRpcInterface::new(
  327. datastore_path.clone(),
  328. broadcast_snd,
  329. nickname.unwrap(),
  330. workspaces.clone(),
  331. p2p.clone(),
  332. ));
  333. let _ex = executor.clone();
  334. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface, _ex)).detach();
  335. // Signal handling for graceful termination.
  336. let (signals_handler, signals_task) = SignalHandler::new()?;
  337. signals_handler.wait_termination(signals_task).await?;
  338. info!("Caught termination signal, cleaning up and exiting...");
  339. model_clone.lock().await.save_tree(&datastore_path)?;
  340. p2p.stop().await;
  341. Ok(())
  342. }