main.rs 13 KB

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