main.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. collections::HashMap,
  20. env,
  21. ffi::CString,
  22. fs::{create_dir_all, remove_dir_all},
  23. io::{stdin, Write},
  24. path::Path,
  25. sync::{Arc, OnceLock},
  26. };
  27. use crypto_box::{
  28. aead::{Aead, AeadCore},
  29. ChaChaBox, SecretKey,
  30. };
  31. use darkfi_serial::{
  32. async_trait, deserialize, deserialize_async_partial, serialize, serialize_async,
  33. SerialDecodable, SerialEncodable,
  34. };
  35. use futures::{select, FutureExt};
  36. use libc::mkfifo;
  37. use log::{debug, error, info};
  38. use rand::rngs::OsRng;
  39. use smol::{fs, lock::RwLock, stream::StreamExt};
  40. use structopt_toml::StructOptToml;
  41. use tinyjson::JsonValue;
  42. use darkfi::{
  43. async_daemonize,
  44. event_graph::{
  45. proto::{EventPut, ProtocolEventGraph},
  46. Event, EventGraph, EventGraphPtr, NULL_ID,
  47. },
  48. net::{P2p, P2pPtr, SESSION_ALL},
  49. rpc::{
  50. jsonrpc::JsonSubscriber,
  51. server::{listen_and_serve, RequestHandler},
  52. },
  53. system::{sleep, StoppableTask},
  54. util::path::expand_path,
  55. Error, Result,
  56. };
  57. mod jsonrpc;
  58. mod settings;
  59. use taud::{
  60. error::{TaudError, TaudResult},
  61. task_info::{TaskEvent, TaskInfo},
  62. util::pipe_write,
  63. };
  64. use crate::{
  65. jsonrpc::JsonRpcInterface,
  66. settings::{Args, CONFIG_FILE, CONFIG_FILE_CONTENTS},
  67. };
  68. fn get_workspaces(settings: &Args) -> Result<HashMap<String, ChaChaBox>> {
  69. let mut workspaces = HashMap::new();
  70. for workspace in settings.workspaces.iter() {
  71. let workspace: Vec<&str> = workspace.split(':').collect();
  72. let (workspace, secret) = (workspace[0], workspace[1]);
  73. let bytes: [u8; 32] = bs58::decode(secret)
  74. .into_vec()?
  75. .try_into()
  76. .map_err(|_| Error::ParseFailed("Parse secret key failed"))?;
  77. let secret = crypto_box::SecretKey::from(bytes);
  78. let public = secret.public_key();
  79. let chacha_box = crypto_box::ChaChaBox::new(&public, &secret);
  80. workspaces.insert(workspace.to_string(), chacha_box);
  81. }
  82. Ok(workspaces)
  83. }
  84. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  85. pub struct EncryptedTask {
  86. payload: String,
  87. }
  88. fn encrypt_task(
  89. task: &TaskInfo,
  90. chacha_box: &ChaChaBox,
  91. rng: &mut OsRng,
  92. ) -> TaudResult<EncryptedTask> {
  93. debug!(target: "taud", "start encrypting task");
  94. let nonce = ChaChaBox::generate_nonce(rng);
  95. let payload = &serialize(task)[..];
  96. let mut payload = chacha_box.encrypt(&nonce, payload)?;
  97. let mut concat = vec![];
  98. concat.append(&mut nonce.as_slice().to_vec());
  99. concat.append(&mut payload);
  100. let payload = bs58::encode(concat.clone()).into_string();
  101. Ok(EncryptedTask { payload })
  102. }
  103. fn try_decrypt_task(encrypt_task: &EncryptedTask, chacha_box: &ChaChaBox) -> TaudResult<TaskInfo> {
  104. debug!(target: "taud", "start decrypting task");
  105. let bytes = match bs58::decode(&encrypt_task.payload).into_vec() {
  106. Ok(v) => v,
  107. Err(_) => return Err(TaudError::DecryptionError("Error decoding payload".to_string())),
  108. };
  109. if bytes.len() < 25 {
  110. return Err(TaudError::DecryptionError("Invalid bytes length".to_string()))
  111. }
  112. // Try extracting the nonce
  113. let nonce = bytes[0..24].into();
  114. // Take the remaining ciphertext
  115. let message = &bytes[24..];
  116. // let nonce = encrypt_task.nonce.as_slice();
  117. let decrypted_task = chacha_box.decrypt(nonce, message)?;
  118. let task = deserialize(&decrypted_task)?;
  119. Ok(task)
  120. }
  121. #[allow(clippy::too_many_arguments)]
  122. async fn start_sync_loop(
  123. event_graph: EventGraphPtr,
  124. broadcast_rcv: smol::channel::Receiver<TaskInfo>,
  125. workspaces: Arc<HashMap<String, ChaChaBox>>,
  126. datastore_path: std::path::PathBuf,
  127. piped: bool,
  128. p2p: P2pPtr,
  129. last_sent: RwLock<blake3::Hash>,
  130. seen: OnceLock<sled::Tree>,
  131. ) -> TaudResult<()> {
  132. let incoming = event_graph.event_sub.clone().subscribe().await;
  133. let seen_events = seen.get().unwrap();
  134. loop {
  135. select! {
  136. // Process message from Tau client
  137. task_event = broadcast_rcv.recv().fuse() => {
  138. let tk = task_event.map_err(Error::from)?;
  139. if workspaces.contains_key(&tk.workspace) {
  140. let chacha_box = workspaces.get(&tk.workspace).unwrap();
  141. let encrypted_task = encrypt_task(&tk, chacha_box, &mut OsRng)?;
  142. info!(target: "taud", "Send the task: ref: {}", tk.ref_id);
  143. // Build a DAG event and return it.
  144. let event = Event::new(
  145. serialize_async(&encrypted_task).await,
  146. &event_graph,
  147. )
  148. .await;
  149. // Update the last sent event.
  150. // let event_id = event.id();
  151. // *last_sent.write().await = event_id;
  152. // If it fails for some reason, for now, we just note it
  153. // and pass.
  154. if let Err(e) = event_graph.dag_insert(&[event.clone()]).await {
  155. error!(target: "taud", "Failed inserting new event to DAG: {}", e);
  156. } else {
  157. // We sent this, so it should be considered seen.
  158. // TODO: should we save task on send or on receive?
  159. // on receive better because it's garanteed your event is out there
  160. // debug!("Marking event {} as seen", event_id);
  161. // seen.get().unwrap().insert(event_id.as_bytes(), &[]).unwrap();
  162. // Otherwise, broadcast it
  163. p2p.broadcast(&EventPut(event)).await;
  164. }
  165. }
  166. }
  167. // Process message from the network. These should only be EncryptedTask.
  168. task_event = incoming.receive().fuse() => {
  169. let event_id = task_event.id();
  170. if *last_sent.read().await == event_id {
  171. continue
  172. }
  173. if seen_events.contains_key(event_id.as_bytes()).unwrap() {
  174. continue
  175. }
  176. // Try to deserialize the `Event`'s content into a `EncryptedTask`
  177. let enc_task: EncryptedTask = match deserialize_async_partial(task_event.content()).await {
  178. Ok((v, _)) => v,
  179. Err(e) => {
  180. error!(target: "taud", "[TAUD] Failed deserializing incoming EncryptedTask event: {}", e);
  181. continue
  182. }
  183. };
  184. on_receive_task(&enc_task, &datastore_path, &workspaces, piped)
  185. .await?;
  186. }
  187. }
  188. }
  189. }
  190. async fn on_receive_task(
  191. task: &EncryptedTask,
  192. datastore_path: &Path,
  193. workspaces: &HashMap<String, ChaChaBox>,
  194. piped: bool,
  195. ) -> TaudResult<()> {
  196. for (workspace, chacha_box) in workspaces.iter() {
  197. let task = try_decrypt_task(task, chacha_box);
  198. if let Err(e) = task {
  199. debug!(target: "taud", "Unable to decrypt the task: {}", e);
  200. continue
  201. }
  202. let mut task = task.unwrap();
  203. info!(target: "taud", "Save the task: ref: {}", task.ref_id);
  204. task.workspace = workspace.clone();
  205. if piped {
  206. // if we can't load the task then it's a new task.
  207. // otherwise it's a modification.
  208. match TaskInfo::load(&task.ref_id, datastore_path) {
  209. Ok(loaded_task) => {
  210. let loaded_events = loaded_task.events;
  211. let mut events = task.events.clone();
  212. events.retain(|ev| !loaded_events.contains(ev));
  213. let file = "/tmp/tau_pipe";
  214. let mut pipe_write = pipe_write(file)?;
  215. let mut task_clone = task.clone();
  216. task_clone.events = events;
  217. let json: JsonValue = (&task_clone).into();
  218. pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
  219. }
  220. Err(_) => {
  221. let file = "/tmp/tau_pipe";
  222. let mut pipe_write = pipe_write(file)?;
  223. let mut task_clone = task.clone();
  224. task_clone.events.push(TaskEvent::new(
  225. "add_task".to_string(),
  226. task_clone.owner.clone(),
  227. "".to_string(),
  228. ));
  229. let json: JsonValue = (&task_clone).into();
  230. pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
  231. }
  232. }
  233. }
  234. task.save(datastore_path)?;
  235. }
  236. Ok(())
  237. }
  238. async_daemonize!(realmain);
  239. async fn realmain(settings: Args, executor: Arc<smol::Executor<'static>>) -> Result<()> {
  240. let datastore_path = expand_path(&settings.datastore)?;
  241. let nickname =
  242. if settings.nickname.is_some() { settings.nickname.clone() } else { env::var("USER").ok() };
  243. if settings.refresh {
  244. println!("Removing local data in: {:?} (yes/no)? ", datastore_path);
  245. let mut confirm = String::new();
  246. stdin().read_line(&mut confirm).expect("Failed to read line");
  247. let confirm = confirm.to_lowercase();
  248. let confirm = confirm.trim();
  249. if confirm == "yes" || confirm == "y" {
  250. remove_dir_all(datastore_path).unwrap_or(());
  251. println!("Local data removed successfully.");
  252. } else {
  253. error!(target: "taud", "Unexpected Value: {}", confirm);
  254. }
  255. return Ok(())
  256. }
  257. if nickname.is_none() {
  258. error!(target: "taud", "Provide a nickname in config file");
  259. return Ok(())
  260. }
  261. if settings.piped {
  262. let file = "/tmp/tau_pipe";
  263. let path = CString::new(file).unwrap();
  264. unsafe { mkfifo(path.as_ptr(), 0o644) };
  265. }
  266. // mkdir datastore_path if not exists
  267. create_dir_all(datastore_path.clone())?;
  268. create_dir_all(datastore_path.join("month"))?;
  269. create_dir_all(datastore_path.join("task"))?;
  270. if settings.generate {
  271. println!("Generating a new workspace");
  272. loop {
  273. println!("Name for the new workspace: ");
  274. let mut workspace = String::new();
  275. stdin().read_line(&mut workspace).expect("Failed to read line");
  276. let workspace = workspace.to_lowercase();
  277. let workspace = workspace.trim();
  278. if workspace.is_empty() && workspace.len() < 3 {
  279. error!(target: "taud", "Wrong workspace try again");
  280. continue
  281. }
  282. let secret_key = SecretKey::generate(&mut OsRng);
  283. let encoded = bs58::encode(secret_key.to_bytes());
  284. println!("workspace: {}:{}", workspace, encoded.into_string());
  285. println!("Please add it to the config file.");
  286. break
  287. }
  288. return Ok(())
  289. }
  290. let workspaces = Arc::new(get_workspaces(&settings)?);
  291. if workspaces.is_empty() {
  292. error!(target: "taud", "Please add at least one workspace to the config file.");
  293. println!("Run `$ taud --generate` to generate new workspace.");
  294. return Ok(())
  295. }
  296. info!("Initializing taud node");
  297. // Create datastore path if not there already.
  298. let datastore = expand_path(&settings.datastore)?;
  299. fs::create_dir_all(&datastore).await?;
  300. info!("Instantiating event DAG");
  301. let sled_db = sled::open(datastore)?;
  302. let p2p = P2p::new(settings.net.into(), executor.clone()).await;
  303. let event_graph =
  304. EventGraph::new(p2p.clone(), sled_db.clone(), "taud_dag", 0, executor.clone()).await?;
  305. info!("Registering EventGraph P2P protocol");
  306. let event_graph_ = Arc::clone(&event_graph);
  307. let registry = p2p.protocol_registry();
  308. registry
  309. .register(SESSION_ALL, move |channel, _| {
  310. let event_graph_ = event_graph_.clone();
  311. async move { ProtocolEventGraph::init(event_graph_, channel).await.unwrap() }
  312. })
  313. .await;
  314. let (broadcast_snd, broadcast_rcv) = smol::channel::unbounded::<TaskInfo>();
  315. info!(target: "taud", "Starting P2P network");
  316. p2p.clone().start().await?;
  317. info!(target: "taud", "Waiting for some P2P connections...");
  318. sleep(5).await;
  319. // We'll attempt to sync 5 times
  320. if !settings.skip_dag_sync {
  321. for i in 1..=6 {
  322. info!("Syncing event DAG (attempt #{})", i);
  323. match event_graph.dag_sync().await {
  324. Ok(()) => break,
  325. Err(e) => {
  326. if i == 6 {
  327. error!(target: "taud", "Failed syncing DAG. Exiting.");
  328. p2p.stop().await;
  329. return Err(Error::DagSyncFailed)
  330. } else {
  331. // TODO: Maybe at this point we should prune or something?
  332. // TODO: Or maybe just tell the user to delete the DAG from FS.
  333. error!(target: "taud", "Failed syncing DAG ({}), retrying in 10s...", e);
  334. sleep(10).await;
  335. }
  336. }
  337. }
  338. }
  339. } else {
  340. *event_graph.synced.write().await = true;
  341. }
  342. ////////////////////
  343. // Listner
  344. ////////////////////
  345. info!(target: "taud", "Starting sync loop task");
  346. let last_sent = RwLock::new(NULL_ID);
  347. let seen = OnceLock::new();
  348. seen.set(sled_db.open_tree("tau_db").unwrap()).unwrap();
  349. ////////////////////
  350. // get history
  351. ////////////////////
  352. let dag_events = event_graph.order_events().await;
  353. let seen_events = seen.get().unwrap();
  354. for event_id in dag_events.iter() {
  355. // If it was seen, skip
  356. if seen_events.contains_key(event_id.as_bytes()).unwrap() {
  357. continue
  358. }
  359. // Get the event from the DAG
  360. let event = event_graph.dag_get(event_id).await.unwrap().unwrap();
  361. // Try to deserialize it. (Here we skip errors)
  362. let Ok((enc_task, _)) = deserialize_async_partial(event.content()).await else { continue };
  363. // Potentially decrypt the privmsg
  364. on_receive_task(&enc_task, &datastore_path, &workspaces, false).await.unwrap();
  365. debug!(target: "taud", "Marking event {} as seen", event_id);
  366. seen_events.insert(event_id.as_bytes(), &[]).unwrap();
  367. }
  368. let sync_loop_task = StoppableTask::new();
  369. sync_loop_task.clone().start(
  370. start_sync_loop(
  371. event_graph.clone(),
  372. broadcast_rcv,
  373. workspaces.clone(),
  374. datastore_path.clone(),
  375. settings.piped,
  376. p2p.clone(),
  377. last_sent,
  378. seen.clone(),
  379. ),
  380. |res| async {
  381. match res {
  382. Ok(()) | Err(TaudError::Darkfi(Error::DetachedTaskStopped)) => { /* Do nothing */ }
  383. Err(e) => error!(target: "taud", "Failed starting sync loop task: {}", e),
  384. }
  385. },
  386. TaudError::Darkfi(Error::DetachedTaskStopped),
  387. executor.clone(),
  388. );
  389. // ==============
  390. // p2p dnet setup
  391. // ==============
  392. info!(target: "taud", "Starting dnet subs task");
  393. let json_sub = JsonSubscriber::new("dnet.subscribe_events");
  394. let json_sub_ = json_sub.clone();
  395. let p2p_ = p2p.clone();
  396. let dnet_task = StoppableTask::new();
  397. dnet_task.clone().start(
  398. async move {
  399. let dnet_sub = p2p_.dnet_subscribe().await;
  400. loop {
  401. let event = dnet_sub.receive().await;
  402. debug!(target: "taud", "Got dnet event: {:?}", event);
  403. json_sub_.notify(vec![event.into()].into()).await;
  404. }
  405. },
  406. |res| async {
  407. match res {
  408. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  409. Err(e) => {
  410. error!(target: "taud", "Failed starting dnet subs task: {}", e)
  411. }
  412. }
  413. },
  414. Error::DetachedTaskStopped,
  415. executor.clone(),
  416. );
  417. info!("Starting deg subs task");
  418. let deg_sub = JsonSubscriber::new("deg.subscribe_events");
  419. let deg_sub_ = deg_sub.clone();
  420. let event_graph_ = event_graph.clone();
  421. let deg_task = StoppableTask::new();
  422. deg_task.clone().start(
  423. async move {
  424. let deg_sub = event_graph_.deg_subscribe().await;
  425. loop {
  426. let event = deg_sub.receive().await;
  427. debug!("Got deg event: {:?}", event);
  428. deg_sub_.notify(vec![event.into()].into()).await;
  429. }
  430. },
  431. |res| async {
  432. match res {
  433. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  434. Err(e) => panic!("{}", e),
  435. }
  436. },
  437. Error::DetachedTaskStopped,
  438. executor.clone(),
  439. );
  440. //
  441. // RPC interface
  442. //
  443. let rpc_interface = Arc::new(JsonRpcInterface::new(
  444. datastore_path.clone(),
  445. broadcast_snd,
  446. nickname.unwrap(),
  447. workspaces.clone(),
  448. p2p.clone(),
  449. event_graph.clone(),
  450. json_sub,
  451. deg_sub,
  452. ));
  453. let rpc_task = StoppableTask::new();
  454. rpc_task.clone().start(
  455. listen_and_serve(settings.rpc_listen, rpc_interface.clone(), None, executor.clone()),
  456. |res| async move {
  457. match res {
  458. Ok(()) | Err(Error::RpcServerStopped) => rpc_interface.stop_connections().await,
  459. Err(e) => error!(target: "taud", "Failed starting JSON-RPC server: {}", e),
  460. }
  461. },
  462. Error::RpcServerStopped,
  463. executor.clone(),
  464. );
  465. // Signal handling for graceful termination.
  466. let (signals_handler, signals_task) = SignalHandler::new(executor)?;
  467. signals_handler.wait_termination(signals_task).await?;
  468. info!("Caught termination signal, cleaning up and exiting...");
  469. info!(target: "taud", "Stopping JSON-RPC server...");
  470. rpc_task.stop().await;
  471. info!(target: "taud", "Stopping sync loop task...");
  472. sync_loop_task.stop().await;
  473. p2p.stop().await;
  474. Ok(())
  475. }