main.rs 19 KB

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