main.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. use async_std::sync::{Arc, Mutex};
  2. use std::{
  3. fs::{create_dir_all, read_dir, remove_dir_all, remove_file},
  4. io::stdin,
  5. path::{Path, PathBuf},
  6. };
  7. use async_executor::Executor;
  8. use crypto_box::{
  9. aead::{Aead, AeadCore},
  10. rand_core::OsRng,
  11. SalsaBox, SecretKey,
  12. };
  13. use futures::{select, FutureExt};
  14. use fxhash::FxHashMap;
  15. use log::{error, info, warn};
  16. use serde::Deserialize;
  17. use sha2::Digest;
  18. use smol::future;
  19. use structopt::StructOpt;
  20. use structopt_toml::StructOptToml;
  21. use unicode_segmentation::UnicodeSegmentation;
  22. use url::Url;
  23. use darkfi::{
  24. async_daemonize,
  25. net::{self, settings::SettingsOpt},
  26. raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
  27. rpc::server::listen_and_serve,
  28. util::{
  29. cli::{get_log_config, get_log_level, spawn_config},
  30. expand_path,
  31. file::{load_file, load_json_file, save_file, save_json_file},
  32. path::get_config_path,
  33. serial::{deserialize, serialize, SerialDecodable, SerialEncodable},
  34. },
  35. Error, Result,
  36. };
  37. mod jsonrpc;
  38. mod lcs;
  39. mod patch;
  40. use jsonrpc::JsonRpcInterface;
  41. use lcs::Lcs;
  42. use patch::{OpMethod, Patch};
  43. type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
  44. pub const CONFIG_FILE: &str = "darkwiki.toml";
  45. pub const CONFIG_FILE_CONTENTS: &str = include_str!("../darkwiki.toml");
  46. /// darkwikid cli
  47. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  48. #[serde(default)]
  49. #[structopt(name = "darkwikid")]
  50. pub struct Args {
  51. /// Sets a custom config file
  52. #[structopt(long)]
  53. pub config: Option<String>,
  54. /// Sets Docs Path
  55. #[structopt(long, default_value = "~/darkwiki")]
  56. pub docs: String,
  57. /// Sets Author Name for Patch
  58. #[structopt(long, default_value = "NONE")]
  59. pub author: String,
  60. /// Secret Key To Encrypt/Decrypt Patches
  61. #[structopt(long, default_value = "")]
  62. pub secret: String,
  63. /// Generate A New Secret Key
  64. #[structopt(long)]
  65. pub keygen: bool,
  66. /// Clean all the local data in docs path
  67. /// (BE CAREFULL) Check the docs path in the config file before running this
  68. #[structopt(long)]
  69. pub refresh: bool,
  70. /// JSON-RPC Listen URL
  71. #[structopt(long = "rpc", default_value = "tcp://127.0.0.1:24330")]
  72. pub rpc_listen: Url,
  73. #[structopt(flatten)]
  74. pub net: SettingsOpt,
  75. /// Increase Verbosity
  76. #[structopt(short, parse(from_occurrences))]
  77. pub verbose: u8,
  78. }
  79. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  80. pub struct EncryptedPatch {
  81. nonce: Vec<u8>,
  82. payload: Vec<u8>,
  83. }
  84. fn encrypt_patch(
  85. patch: &Patch,
  86. salsa_box: &SalsaBox,
  87. rng: &mut crypto_box::rand_core::OsRng,
  88. ) -> Result<EncryptedPatch> {
  89. let nonce = SalsaBox::generate_nonce(rng);
  90. let payload = &serialize(patch)[..];
  91. let payload = salsa_box
  92. .encrypt(&nonce, payload)
  93. .map_err(|_| Error::ParseFailed("Encrypting Patch failed"))?;
  94. let nonce = nonce.to_vec();
  95. Ok(EncryptedPatch { nonce, payload })
  96. }
  97. fn decrypt_patch(encrypt_patch: &EncryptedPatch, salsa_box: &SalsaBox) -> Result<Patch> {
  98. let nonce = encrypt_patch.nonce.as_slice();
  99. let decrypted_patch = salsa_box
  100. .decrypt(nonce.into(), &encrypt_patch.payload[..])
  101. .map_err(|_| Error::ParseFailed("Decrypting Patch failed"))?;
  102. let patch = deserialize(&decrypted_patch)?;
  103. Ok(patch)
  104. }
  105. pub struct DarkWikiSettings {
  106. author: String,
  107. docs_path: PathBuf,
  108. datastore_path: PathBuf,
  109. }
  110. fn str_to_chars(s: &str) -> Vec<&str> {
  111. s.graphemes(true).collect::<Vec<&str>>()
  112. }
  113. fn path_to_id(path: &str) -> String {
  114. let mut hasher = sha2::Sha256::new();
  115. hasher.update(path);
  116. bs58::encode(hex::encode(hasher.finalize())).into_string()
  117. }
  118. fn get_docs_paths(files: &mut Vec<PathBuf>, path: &Path, parent: Option<&Path>) -> Result<()> {
  119. let docs = read_dir(&path)?;
  120. let docs = docs.filter(|d| d.is_ok()).map(|d| d.unwrap().path()).collect::<Vec<PathBuf>>();
  121. for doc in docs {
  122. if let Some(f) = doc.file_name() {
  123. let file_name = PathBuf::from(f);
  124. let file_name =
  125. if let Some(parent) = parent { parent.join(file_name) } else { file_name };
  126. if doc.is_file() {
  127. if let Some(ext) = doc.extension() {
  128. if ext == "md" {
  129. files.push(file_name);
  130. }
  131. }
  132. } else if doc.is_dir() {
  133. if f == ".log" {
  134. continue
  135. }
  136. get_docs_paths(files, &doc, Some(&file_name))?;
  137. }
  138. }
  139. }
  140. Ok(())
  141. }
  142. fn is_delete_patch(patch: &Patch) -> bool {
  143. if patch.ops().0.len() != 1 {
  144. return false
  145. }
  146. if let OpMethod::Delete(d) = patch.ops().0[0] {
  147. if patch.base.len() as u64 == d {
  148. return true
  149. }
  150. }
  151. false
  152. }
  153. struct Darkwiki {
  154. settings: DarkWikiSettings,
  155. #[allow(clippy::type_complexity)]
  156. rpc: (
  157. async_channel::Sender<Vec<Vec<(String, String)>>>,
  158. async_channel::Receiver<(String, bool, Vec<String>)>,
  159. ),
  160. raft: (async_channel::Sender<EncryptedPatch>, async_channel::Receiver<EncryptedPatch>),
  161. salsa_box: SalsaBox,
  162. }
  163. impl Darkwiki {
  164. async fn start(&self) -> Result<()> {
  165. let mut rng = crypto_box::rand_core::OsRng;
  166. loop {
  167. select! {
  168. val = self.rpc.1.recv().fuse() => {
  169. let (cmd, dry, files) = val?;
  170. match cmd.as_str() {
  171. "update" => {
  172. self.on_receive_update(dry, files, &mut rng).await?;
  173. },
  174. "restore" => {
  175. self.on_receive_restore(dry, files).await?;
  176. },
  177. _ => {}
  178. }
  179. }
  180. patch = self.raft.1.recv().fuse() => {
  181. self.on_receive_patch(&patch?)?;
  182. }
  183. }
  184. }
  185. }
  186. fn on_receive_patch(&self, received_patch: &EncryptedPatch) -> Result<()> {
  187. let received_patch = decrypt_patch(received_patch, &self.salsa_box)?;
  188. info!("Receive a {:?}", received_patch);
  189. let sync_id_path = self.settings.datastore_path.join("sync").join(&received_patch.id);
  190. let local_id_path = self.settings.datastore_path.join("local").join(&received_patch.id);
  191. if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
  192. if sync_patch.timestamp == received_patch.timestamp {
  193. return Ok(())
  194. }
  195. if let Ok(local_patch) = load_json_file::<Patch>(&local_id_path) {
  196. if local_patch.timestamp == sync_patch.timestamp {
  197. sync_patch.base = local_patch.to_string();
  198. sync_patch.set_ops(received_patch.ops());
  199. } else {
  200. sync_patch.extend_ops(received_patch.ops());
  201. }
  202. } else {
  203. sync_patch.extend_ops(received_patch.ops());
  204. }
  205. sync_patch.timestamp = received_patch.timestamp;
  206. sync_patch.author = received_patch.author;
  207. save_json_file::<Patch>(&sync_id_path, &sync_patch)?;
  208. } else if !received_patch.base.is_empty() {
  209. save_json_file::<Patch>(&sync_id_path, &received_patch)?;
  210. }
  211. Ok(())
  212. }
  213. async fn on_receive_update(
  214. &self,
  215. dry: bool,
  216. files: Vec<String>,
  217. rng: &mut OsRng,
  218. ) -> Result<()> {
  219. let (patches, local, sync, merge) = self.update(dry, files)?;
  220. if !dry {
  221. for patch in patches {
  222. info!("Send a {:?}", patch);
  223. let encrypt_patch = encrypt_patch(&patch, &self.salsa_box, rng)?;
  224. self.raft.0.send(encrypt_patch).await?;
  225. }
  226. }
  227. let local: Vec<(String, String)> =
  228. local.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
  229. let sync: Vec<(String, String)> =
  230. sync.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
  231. let merge: Vec<(String, String)> =
  232. merge.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
  233. self.rpc.0.send(vec![local, sync, merge]).await?;
  234. Ok(())
  235. }
  236. async fn on_receive_restore(&self, dry: bool, files_name: Vec<String>) -> Result<()> {
  237. let patches = self.restore(dry, files_name)?;
  238. let patches: Vec<(String, String)> =
  239. patches.iter().map(|p| (p.path.to_owned(), p.to_string())).collect();
  240. self.rpc.0.send(vec![patches]).await?;
  241. Ok(())
  242. }
  243. fn restore(&self, dry: bool, files_name: Vec<String>) -> Result<Vec<Patch>> {
  244. let local_path = self.settings.datastore_path.join("local");
  245. let docs_path = self.settings.docs_path.clone();
  246. let mut patches = vec![];
  247. let local_files = read_dir(&local_path)?;
  248. for file in local_files {
  249. let file_id = file?.file_name();
  250. let file_path = local_path.join(&file_id);
  251. let local_patch: Patch = load_json_file(&file_path)?;
  252. if !files_name.is_empty() && !files_name.contains(&local_patch.path.to_string()) {
  253. continue
  254. }
  255. if let Ok(doc) = load_file(&docs_path.join(&local_patch.path)) {
  256. if local_patch.to_string() == doc {
  257. continue
  258. }
  259. }
  260. if !dry {
  261. self.save_doc(&local_patch.path, &local_patch.to_string())?;
  262. }
  263. patches.push(local_patch);
  264. }
  265. Ok(patches)
  266. }
  267. fn update(&self, dry: bool, files_name: Vec<String>) -> Result<Patches> {
  268. let mut patches: Vec<Patch> = vec![];
  269. let mut local_patches: Vec<Patch> = vec![];
  270. let mut sync_patches: Vec<Patch> = vec![];
  271. let mut merge_patches: Vec<Patch> = vec![];
  272. let local_path = self.settings.datastore_path.join("local");
  273. let sync_path = self.settings.datastore_path.join("sync");
  274. let docs_path = self.settings.docs_path.clone();
  275. // save and compare docs in darkwiki and local dirs
  276. // then merged with sync patches if any received
  277. let mut docs = vec![];
  278. get_docs_paths(&mut docs, &docs_path, None)?;
  279. for doc in docs {
  280. let doc_path = doc.to_str().unwrap();
  281. if !files_name.is_empty() && !files_name.contains(&doc_path.to_string()) {
  282. continue
  283. }
  284. // load doc content
  285. let edit = load_file(&docs_path.join(doc_path))?;
  286. if edit.is_empty() {
  287. continue
  288. }
  289. let doc_id = path_to_id(doc_path);
  290. // create new patch
  291. let mut new_patch = Patch::new(doc_path, &doc_id, &self.settings.author);
  292. // check for any changes found with local doc and darkwiki doc
  293. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
  294. // no changes found
  295. if local_patch.to_string() == edit {
  296. continue
  297. }
  298. // check the differences with LCS algorithm
  299. let local_patch_str = local_patch.to_string();
  300. let lcs = Lcs::new(&local_patch_str, &edit);
  301. let lcs_ops = lcs.ops();
  302. // add the change ops to the new patch
  303. for op in lcs_ops {
  304. new_patch.add_op(&op);
  305. }
  306. new_patch.base = local_patch.to_string();
  307. local_patches.push(new_patch.clone());
  308. let mut b_patch = new_patch.clone();
  309. b_patch.base = "".to_string();
  310. patches.push(b_patch);
  311. // check if the same doc has received patch from the network
  312. if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
  313. if !is_delete_patch(&sync_patch) {
  314. if sync_patch.timestamp != local_patch.timestamp {
  315. sync_patches.push(sync_patch.clone());
  316. let sync_patch_t = new_patch.transform(&sync_patch);
  317. new_patch = new_patch.merge(&sync_patch_t);
  318. if !dry {
  319. self.save_doc(doc_path, &new_patch.to_string())?;
  320. }
  321. merge_patches.push(new_patch.clone());
  322. }
  323. } else {
  324. merge_patches.push(sync_patch);
  325. patches = vec![];
  326. }
  327. }
  328. } else {
  329. new_patch.base = edit.to_string();
  330. local_patches.push(new_patch.clone());
  331. patches.push(new_patch.clone());
  332. };
  333. if !dry {
  334. save_json_file(&local_path.join(&doc_id), &new_patch)?;
  335. save_json_file(&sync_path.join(doc_id), &new_patch)?;
  336. }
  337. }
  338. // check if a new patch received
  339. // and save the new changes in both local and darkwiki dirs
  340. let sync_files = read_dir(&sync_path)?;
  341. for file in sync_files {
  342. let file_id = file?.file_name();
  343. let file_path = sync_path.join(&file_id);
  344. let sync_patch: Patch = load_json_file(&file_path)?;
  345. if is_delete_patch(&sync_patch) {
  346. if local_path.join(&sync_patch.id).exists() {
  347. sync_patches.push(sync_patch.clone());
  348. }
  349. if !dry {
  350. remove_file(docs_path.join(&sync_patch.path)).unwrap_or(());
  351. remove_file(local_path.join(&sync_patch.id)).unwrap_or(());
  352. remove_file(file_path).unwrap_or(());
  353. }
  354. continue
  355. }
  356. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&file_id)) {
  357. if local_patch.timestamp == sync_patch.timestamp {
  358. continue
  359. }
  360. }
  361. if !files_name.is_empty() && !files_name.contains(&sync_patch.path.to_string()) {
  362. continue
  363. }
  364. if !dry {
  365. self.save_doc(&sync_patch.path, &sync_patch.to_string())?;
  366. save_json_file(&local_path.join(file_id), &sync_patch)?;
  367. }
  368. if !sync_patches.contains(&sync_patch) {
  369. sync_patches.push(sync_patch);
  370. }
  371. }
  372. // check if any doc removed from ~/darkwiki
  373. let local_files = read_dir(&local_path)?;
  374. for file in local_files {
  375. let file_id = file?.file_name();
  376. let file_path = local_path.join(&file_id);
  377. let local_patch: Patch = load_json_file(&file_path)?;
  378. if !files_name.is_empty() && !files_name.contains(&local_patch.path.to_string()) {
  379. continue
  380. }
  381. if !docs_path.join(&local_patch.path).exists() {
  382. let mut new_patch =
  383. Patch::new(&local_patch.path, &local_patch.id, &self.settings.author);
  384. new_patch.add_op(&OpMethod::Delete(local_patch.to_string().len() as u64));
  385. patches.push(new_patch.clone());
  386. new_patch.base = local_patch.base;
  387. local_patches.push(new_patch);
  388. if !dry {
  389. remove_file(file_path).unwrap_or(());
  390. }
  391. }
  392. }
  393. Ok((patches, local_patches, sync_patches, merge_patches))
  394. }
  395. fn save_doc(&self, path: &str, edit: &str) -> Result<()> {
  396. let path = self.settings.docs_path.join(path);
  397. if let Some(p) = path.parent() {
  398. if !p.exists() && !p.to_str().unwrap().is_empty() {
  399. create_dir_all(p)?;
  400. }
  401. }
  402. save_file(&path, edit)
  403. }
  404. }
  405. async_daemonize!(realmain);
  406. async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
  407. let docs_path = expand_path(&settings.docs)?;
  408. let datastore_path = expand_path(docs_path.join(".log").to_str().unwrap())?;
  409. if settings.refresh {
  410. println!("Removing local docs in: {:?} (yes/no)? ", docs_path);
  411. let mut confirm = String::new();
  412. stdin().read_line(&mut confirm).ok().expect("Failed to read line");
  413. let confirm = confirm.to_lowercase();
  414. let confirm = confirm.trim();
  415. if confirm == "yes" || confirm == "y" {
  416. remove_dir_all(docs_path).unwrap_or(());
  417. println!("Local docs get removed");
  418. } else {
  419. error!("Unexpected Value: {}", confirm);
  420. }
  421. return Ok(())
  422. }
  423. create_dir_all(docs_path.clone())?;
  424. create_dir_all(datastore_path.clone())?;
  425. create_dir_all(datastore_path.join("local"))?;
  426. create_dir_all(datastore_path.join("sync"))?;
  427. if settings.keygen {
  428. info!("Generating a new secret key");
  429. let mut rng = crypto_box::rand_core::OsRng;
  430. let secret_key = SecretKey::generate(&mut rng);
  431. let encoded = bs58::encode(secret_key.as_bytes());
  432. println!("Secret key: {}", encoded.into_string());
  433. return Ok(())
  434. }
  435. let bytes: [u8; 32] = bs58::decode(settings.secret)
  436. .into_vec()?
  437. .try_into()
  438. .map_err(|_| Error::ParseFailed("Parse secret key failed"))?;
  439. let secret = crypto_box::SecretKey::from(bytes);
  440. let public = secret.public_key();
  441. let salsa_box = crypto_box::SalsaBox::new(&public, &secret);
  442. let (rpc_sx, rpc_rv) = async_channel::unbounded::<(String, bool, Vec<String>)>();
  443. let (notify_sx, notify_rv) = async_channel::unbounded::<Vec<Vec<(String, String)>>>();
  444. //
  445. // RPC
  446. //
  447. let rpc_interface = Arc::new(JsonRpcInterface::new(rpc_sx, notify_rv));
  448. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface)).detach();
  449. //
  450. // Raft
  451. //
  452. let net_settings = settings.net;
  453. let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
  454. let datastore_raft = datastore_path.join("darkwiki.db");
  455. let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
  456. let mut raft = Raft::<EncryptedPatch>::new(raft_settings, seen_net_msgs.clone())?;
  457. //
  458. // P2p setup
  459. //
  460. let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
  461. let p2p = net::P2p::new(net_settings.into()).await;
  462. let p2p = p2p.clone();
  463. let registry = p2p.protocol_registry();
  464. let raft_node_id = raft.id();
  465. registry
  466. .register(net::SESSION_ALL, move |channel, p2p| {
  467. let raft_node_id = raft_node_id.clone();
  468. let sender = p2p_send_channel.clone();
  469. let seen_net_msgs_cloned = seen_net_msgs.clone();
  470. async move {
  471. ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
  472. }
  473. })
  474. .await;
  475. p2p.clone().start(executor.clone()).await?;
  476. executor.spawn(p2p.clone().run(executor.clone())).detach();
  477. p2p.clone().wait_for_outbound(executor.clone()).await?;
  478. //
  479. // Darkwiki start
  480. //
  481. let raft_sx = raft.sender();
  482. let raft_rv = raft.receiver();
  483. executor
  484. .spawn(async move {
  485. let darkwiki_settings =
  486. DarkWikiSettings { author: settings.author, datastore_path, docs_path };
  487. let darkwiki = Darkwiki {
  488. settings: darkwiki_settings,
  489. raft: (raft_sx, raft_rv),
  490. rpc: (notify_sx, rpc_rv),
  491. salsa_box,
  492. };
  493. darkwiki.start().await.unwrap_or(());
  494. })
  495. .detach();
  496. //
  497. // Waiting Exit signal
  498. //
  499. let (signal, shutdown) = async_channel::bounded::<()>(1);
  500. ctrlc::set_handler(move || {
  501. warn!(target: "darkwiki", "Catch exit signal");
  502. // cleaning up tasks running in the background
  503. if let Err(e) = async_std::task::block_on(signal.send(())) {
  504. error!("Error on sending exit signal: {}", e);
  505. }
  506. })
  507. .unwrap();
  508. raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
  509. Ok(())
  510. }