main.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2022 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::{
  19. env,
  20. fs::{create_dir_all, remove_dir_all},
  21. io::stdin,
  22. path::Path,
  23. };
  24. use async_std::sync::{Arc, Mutex};
  25. use crypto_box::{
  26. aead::{Aead, AeadCore},
  27. SalsaBox, SecretKey,
  28. };
  29. use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
  30. use futures::{select, FutureExt};
  31. use fxhash::FxHashMap;
  32. use log::{debug, error, info, warn};
  33. use structopt_toml::StructOptToml;
  34. use darkfi::{
  35. async_daemonize, net,
  36. raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
  37. rpc::server::listen_and_serve,
  38. util::path::expand_path,
  39. Error, Result,
  40. };
  41. mod error;
  42. mod jsonrpc;
  43. mod month_tasks;
  44. mod settings;
  45. mod task_info;
  46. mod util;
  47. use crate::{
  48. error::TaudResult,
  49. jsonrpc::JsonRpcInterface,
  50. settings::{Args, CONFIG_FILE, CONFIG_FILE_CONTENTS},
  51. task_info::TaskInfo,
  52. };
  53. fn get_workspaces(settings: &Args) -> Result<FxHashMap<String, SalsaBox>> {
  54. let mut workspaces = FxHashMap::default();
  55. for workspace in settings.workspaces.iter() {
  56. let workspace: Vec<&str> = workspace.split(':').collect();
  57. let (workspace, secret) = (workspace[0], workspace[1]);
  58. let bytes: [u8; 32] = bs58::decode(secret)
  59. .into_vec()?
  60. .try_into()
  61. .map_err(|_| Error::ParseFailed("Parse secret key failed"))?;
  62. let secret = crypto_box::SecretKey::from(bytes);
  63. let public = secret.public_key();
  64. let salsa_box = crypto_box::SalsaBox::new(&public, &secret);
  65. workspaces.insert(workspace.to_string(), salsa_box);
  66. }
  67. Ok(workspaces)
  68. }
  69. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  70. pub struct EncryptedTask {
  71. nonce: Vec<u8>,
  72. payload: Vec<u8>,
  73. }
  74. fn encrypt_task(
  75. task: &TaskInfo,
  76. salsa_box: &SalsaBox,
  77. rng: &mut crypto_box::rand_core::OsRng,
  78. ) -> TaudResult<EncryptedTask> {
  79. debug!("start encrypting task");
  80. let nonce = SalsaBox::generate_nonce(rng);
  81. let payload = &serialize(task)[..];
  82. let payload = salsa_box.encrypt(&nonce, payload)?;
  83. let nonce = nonce.to_vec();
  84. Ok(EncryptedTask { nonce, payload })
  85. }
  86. fn decrypt_task(encrypt_task: &EncryptedTask, salsa_box: &SalsaBox) -> TaudResult<TaskInfo> {
  87. debug!("start decrypting task");
  88. let nonce = encrypt_task.nonce.as_slice();
  89. let decrypted_task = salsa_box.decrypt(nonce.into(), &encrypt_task.payload[..])?;
  90. let task = deserialize(&decrypted_task)?;
  91. Ok(task)
  92. }
  93. async fn start_sync_loop(
  94. broadcast_rcv: smol::channel::Receiver<TaskInfo>,
  95. raft_msgs_sender: smol::channel::Sender<EncryptedTask>,
  96. commits_recv: smol::channel::Receiver<EncryptedTask>,
  97. datastore_path: std::path::PathBuf,
  98. workspaces: FxHashMap<String, SalsaBox>,
  99. mut rng: crypto_box::rand_core::OsRng,
  100. ) -> TaudResult<()> {
  101. loop {
  102. select! {
  103. task = broadcast_rcv.recv().fuse() => {
  104. let tk = task.map_err(Error::from)?;
  105. if workspaces.contains_key(&tk.workspace) {
  106. let salsa_box = workspaces.get(&tk.workspace).unwrap();
  107. let encrypted_task = encrypt_task(&tk, salsa_box, &mut rng)?;
  108. info!(target: "tau", "Send the task: ref: {}", tk.ref_id);
  109. raft_msgs_sender.send(encrypted_task).await.map_err(Error::from)?;
  110. }
  111. }
  112. task = commits_recv.recv().fuse() => {
  113. let task = task.map_err(Error::from)?;
  114. on_receive_task(&task,&datastore_path, &workspaces)
  115. .await?;
  116. }
  117. }
  118. }
  119. }
  120. async fn on_receive_task(
  121. task: &EncryptedTask,
  122. datastore_path: &Path,
  123. workspaces: &FxHashMap<String, SalsaBox>,
  124. ) -> TaudResult<()> {
  125. for (workspace, salsa_box) in workspaces.iter() {
  126. let task = decrypt_task(task, salsa_box);
  127. if let Err(e) = task {
  128. info!("unable to decrypt the task: {}", e);
  129. continue
  130. }
  131. let mut task = task.unwrap();
  132. info!(target: "tau", "Save the task: ref: {}", task.ref_id);
  133. task.workspace = workspace.clone();
  134. task.save(datastore_path)?;
  135. }
  136. Ok(())
  137. }
  138. async_daemonize!(realmain);
  139. async fn realmain(settings: Args, executor: Arc<smol::Executor<'_>>) -> Result<()> {
  140. let datastore_path = expand_path(&settings.datastore)?;
  141. let nickname =
  142. if settings.nickname.is_some() { settings.nickname.clone() } else { env::var("USER").ok() };
  143. if settings.refresh {
  144. println!("Removing local data in: {:?} (yes/no)? ", datastore_path);
  145. let mut confirm = String::new();
  146. stdin().read_line(&mut confirm).expect("Failed to read line");
  147. let confirm = confirm.to_lowercase();
  148. let confirm = confirm.trim();
  149. if confirm == "yes" || confirm == "y" {
  150. remove_dir_all(datastore_path).unwrap_or(());
  151. println!("Local data removed successfully.");
  152. } else {
  153. error!("Unexpected Value: {}", confirm);
  154. }
  155. return Ok(())
  156. }
  157. if nickname.is_none() {
  158. error!("Provide a nickname in config file");
  159. return Ok(())
  160. }
  161. // mkdir datastore_path if not exists
  162. create_dir_all(datastore_path.clone())?;
  163. create_dir_all(datastore_path.join("month"))?;
  164. create_dir_all(datastore_path.join("task"))?;
  165. let rng = crypto_box::rand_core::OsRng;
  166. if settings.generate {
  167. println!("Generating a new workspace");
  168. loop {
  169. println!("Name for the new workspace: ");
  170. let mut workspace = String::new();
  171. stdin().read_line(&mut workspace).expect("Failed to read line");
  172. let workspace = workspace.to_lowercase();
  173. let workspace = workspace.trim();
  174. if workspace.is_empty() && workspace.len() < 3 {
  175. error!("Wrong workspace try again");
  176. continue
  177. }
  178. let mut rng = crypto_box::rand_core::OsRng;
  179. let secret_key = SecretKey::generate(&mut rng);
  180. let encoded = bs58::encode(secret_key.as_bytes());
  181. println!("workspace: {}:{}", workspace, encoded.into_string());
  182. println!("Please add it to the config file.");
  183. break
  184. }
  185. return Ok(())
  186. }
  187. let workspaces = get_workspaces(&settings)?;
  188. if workspaces.is_empty() {
  189. error!("Please add at least one workspace to the config file.");
  190. println!("Run `$ taud --generate` to generate new workspace.");
  191. return Ok(())
  192. }
  193. //
  194. // Raft
  195. //
  196. let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
  197. let datastore_raft = datastore_path.join("tau.db");
  198. let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
  199. let mut raft = Raft::<EncryptedTask>::new(raft_settings, seen_net_msgs.clone())?;
  200. let raft_id = raft.id();
  201. let (broadcast_snd, broadcast_rcv) = smol::channel::unbounded::<TaskInfo>();
  202. //
  203. // P2p setup
  204. //
  205. let mut net_settings = settings.net.clone();
  206. net_settings.app_version = Some(option_env!("CARGO_PKG_VERSION").unwrap_or("").to_string());
  207. let (p2p_send_channel, p2p_recv_channel) = smol::channel::unbounded::<NetMsg>();
  208. let p2p = net::P2p::new(net_settings.into()).await;
  209. let p2p = p2p.clone();
  210. let registry = p2p.protocol_registry();
  211. registry
  212. .register(net::SESSION_ALL, move |channel, p2p| {
  213. let raft_id = raft_id.clone();
  214. let sender = p2p_send_channel.clone();
  215. let seen_net_msgs_cloned = seen_net_msgs.clone();
  216. async move {
  217. ProtocolRaft::init(raft_id, channel, sender, p2p, seen_net_msgs_cloned).await
  218. }
  219. })
  220. .await;
  221. p2p.clone().start(executor.clone()).await?;
  222. executor.spawn(p2p.clone().run(executor.clone())).detach();
  223. //
  224. // RPC interface
  225. //
  226. let rpc_interface = Arc::new(JsonRpcInterface::new(
  227. datastore_path.clone(),
  228. broadcast_snd,
  229. nickname.unwrap(),
  230. workspaces.clone(),
  231. p2p.clone(),
  232. ));
  233. let _ex = executor.clone();
  234. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface, _ex)).detach();
  235. //
  236. // Waiting Exit signal
  237. //
  238. let (signal, shutdown) = smol::channel::bounded::<()>(1);
  239. ctrlc::set_handler(move || {
  240. warn!(target: "tau", "Catch exit signal");
  241. // cleaning up tasks running in the background
  242. if let Err(e) = async_std::task::block_on(signal.send(())) {
  243. error!("Error on sending exit signal: {}", e);
  244. }
  245. })
  246. .unwrap();
  247. executor
  248. .spawn(start_sync_loop(
  249. broadcast_rcv,
  250. raft.sender(),
  251. raft.receiver(),
  252. datastore_path,
  253. workspaces,
  254. rng,
  255. ))
  256. .detach();
  257. raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
  258. Ok(())
  259. }