main.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. use async_std::sync::{Arc, Mutex};
  2. use std::{
  3. fs::{create_dir_all, read_dir},
  4. path::PathBuf,
  5. };
  6. use async_executor::Executor;
  7. use futures::{select, FutureExt};
  8. use fxhash::FxHashMap;
  9. use log::{error, info, warn};
  10. use serde::Deserialize;
  11. use sha2::Digest;
  12. use smol::future;
  13. use structopt::StructOpt;
  14. use structopt_toml::StructOptToml;
  15. use unicode_segmentation::UnicodeSegmentation;
  16. use url::Url;
  17. use darkfi::{
  18. async_daemonize,
  19. net::{self, settings::SettingsOpt},
  20. raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
  21. rpc::server::listen_and_serve,
  22. util::{
  23. cli::{get_log_config, get_log_level, spawn_config},
  24. expand_path,
  25. file::{load_file, load_json_file, save_file, save_json_file},
  26. path::get_config_path,
  27. },
  28. Error, Result,
  29. };
  30. mod error;
  31. mod jsonrpc;
  32. mod patch;
  33. use error::DarkWikiResult;
  34. use jsonrpc::JsonRpcInterface;
  35. use patch::{OpMethod, Patch};
  36. type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
  37. pub const CONFIG_FILE: &str = "darkwiki.toml";
  38. pub const CONFIG_FILE_CONTENTS: &str = include_str!("../darkwiki.toml");
  39. /// darkwikid cli
  40. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  41. #[serde(default)]
  42. #[structopt(name = "darkwikid")]
  43. pub struct Args {
  44. /// Sets a custom config file
  45. #[structopt(long)]
  46. pub config: Option<String>,
  47. /// Sets Datastore Path
  48. #[structopt(long, default_value = "~/.config/darkfi/darkwiki")]
  49. pub datastore: String,
  50. /// Sets Docs Path
  51. #[structopt(long, default_value = "~/darkwiki")]
  52. pub docs: String,
  53. /// Sets Author Name for Patch
  54. #[structopt(long, default_value = "NONE")]
  55. pub author: String,
  56. /// JSON-RPC listen URL
  57. #[structopt(long = "rpc", default_value = "tcp://127.0.0.1:13055")]
  58. pub rpc_listen: Url,
  59. #[structopt(flatten)]
  60. pub net: SettingsOpt,
  61. /// Increase verbosity
  62. #[structopt(short, parse(from_occurrences))]
  63. pub verbose: u8,
  64. }
  65. pub struct DarkWikiSettings {
  66. author: String,
  67. docs_path: PathBuf,
  68. datastore_path: PathBuf,
  69. }
  70. fn str_to_chars(s: &str) -> Vec<&str> {
  71. s.graphemes(true).collect::<Vec<&str>>()
  72. }
  73. fn lcs(a: &str, b: &str) -> Vec<OpMethod> {
  74. let a: Vec<_> = str_to_chars(a);
  75. let b: Vec<_> = str_to_chars(b);
  76. let (na, nb) = (a.len(), b.len());
  77. let mut lengths = vec![vec![0; nb + 1]; na + 1];
  78. for (i, ci) in a.iter().enumerate() {
  79. for (j, cj) in b.iter().enumerate() {
  80. lengths[i + 1][j + 1] =
  81. if ci == cj { lengths[i][j] + 1 } else { lengths[i][j + 1].max(lengths[i + 1][j]) }
  82. }
  83. }
  84. let mut result = Vec::new();
  85. let (mut i, mut j) = (na, nb);
  86. while i > 0 && j > 0 {
  87. if a[i - 1] == b[j - 1] {
  88. result.push(OpMethod::Retain((1) as _));
  89. i -= 1;
  90. j -= 1;
  91. } else if lengths[i - 1][j] > lengths[i][j - 1] {
  92. result.push(OpMethod::Delete((1) as _));
  93. i -= 1;
  94. } else {
  95. result.push(OpMethod::Insert(b[j - 1].to_string()));
  96. j -= 1;
  97. }
  98. }
  99. result.reverse();
  100. result
  101. }
  102. fn on_receive_patch(received_patch: &Patch, settings: &DarkWikiSettings) -> DarkWikiResult<()> {
  103. let sync_id_path = settings.datastore_path.join("sync").join(&received_patch.id);
  104. let local_id_path = settings.datastore_path.join("local").join(&received_patch.id);
  105. if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
  106. if sync_patch.timestamp == received_patch.timestamp {
  107. return Ok(())
  108. }
  109. if let Ok(local_patch) = load_json_file::<Patch>(&local_id_path) {
  110. if local_patch.timestamp == sync_patch.timestamp {
  111. sync_patch.base = local_patch.to_string();
  112. sync_patch.set_ops(received_patch.ops());
  113. } else {
  114. sync_patch.extend_ops(received_patch.ops());
  115. }
  116. }
  117. sync_patch.timestamp = received_patch.timestamp;
  118. sync_patch.author = received_patch.author.clone();
  119. save_json_file::<Patch>(&sync_id_path, &sync_patch)?;
  120. } else if !received_patch.base.is_empty() {
  121. save_json_file::<Patch>(&sync_id_path, received_patch)?;
  122. }
  123. Ok(())
  124. }
  125. fn title_to_id(title: &str) -> String {
  126. let mut hasher = sha2::Sha256::new();
  127. hasher.update(title);
  128. hex::encode(hasher.finalize())
  129. }
  130. fn on_receive_update(settings: &DarkWikiSettings, dry: bool) -> DarkWikiResult<Patches> {
  131. let mut patches: Vec<Patch> = vec![];
  132. let mut local_patches: Vec<Patch> = vec![];
  133. let mut sync_patches: Vec<Patch> = vec![];
  134. let mut merge_patches: Vec<Patch> = vec![];
  135. let local_path = settings.datastore_path.join("local");
  136. let sync_path = settings.datastore_path.join("sync");
  137. let docs_path = settings.docs_path.clone();
  138. // save and compare docs in darkwiki and local dirs
  139. // then merged with sync patches if any received
  140. let docs = read_dir(&docs_path).map_err(Error::from)?;
  141. for doc in docs {
  142. let doc_title = doc.as_ref().unwrap().file_name();
  143. let doc_title = doc_title.to_str().unwrap();
  144. // load doc content
  145. let edit = load_file(&docs_path.join(doc_title)).map_err(Error::from)?;
  146. let edit = edit.trim();
  147. let doc_id = title_to_id(doc_title);
  148. // create new patch
  149. let mut new_patch = Patch::new(doc_title, &doc_id, &settings.author);
  150. // check for any changes found with local doc and darkwiki doc
  151. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
  152. // no changes found
  153. if local_patch.to_string() == edit {
  154. continue
  155. }
  156. // check the differences with LCS algorithm
  157. let lcs_ops = lcs(&local_patch.to_string(), edit);
  158. // add the change ops to the new patch
  159. for op in lcs_ops {
  160. new_patch.add_op(&op);
  161. }
  162. new_patch.base = local_patch.to_string();
  163. local_patches.push(new_patch.clone());
  164. let mut b_patch = new_patch.clone();
  165. b_patch.base = "".to_string();
  166. patches.push(b_patch);
  167. // check if the same doc has received patch from the network
  168. if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
  169. if sync_patch.timestamp != local_patch.timestamp {
  170. sync_patches.push(sync_patch.clone());
  171. let sync_patch_t = new_patch.transform(&sync_patch);
  172. new_patch = new_patch.merge(&sync_patch_t);
  173. if !dry {
  174. save_file(&docs_path.join(doc_title), &new_patch.to_string())?;
  175. }
  176. merge_patches.push(new_patch.clone());
  177. }
  178. }
  179. } else {
  180. new_patch.base = edit.to_string();
  181. local_patches.push(new_patch.clone());
  182. patches.push(new_patch.clone());
  183. };
  184. if !dry {
  185. save_json_file(&local_path.join(&doc_id), &new_patch)?;
  186. save_json_file(&sync_path.join(doc_id), &new_patch)?;
  187. }
  188. }
  189. // check if a new patch received
  190. // and save the new changes in both local and darkwiki dirs
  191. let sync_files = read_dir(&sync_path).map_err(Error::from)?;
  192. for file in sync_files {
  193. let file_id = file.as_ref().unwrap().file_name();
  194. let file_id = file_id.to_str().unwrap();
  195. let file_path = sync_path.join(&file_id);
  196. let sync_patch: Patch = load_json_file(&file_path)?;
  197. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&file_id)) {
  198. if local_patch.timestamp == sync_patch.timestamp {
  199. continue
  200. }
  201. }
  202. if !dry {
  203. save_file(&docs_path.join(&sync_patch.title), &sync_patch.to_string())?;
  204. save_json_file(&local_path.join(file_id), &sync_patch)?;
  205. }
  206. if !sync_patches.contains(&sync_patch) {
  207. sync_patches.push(sync_patch);
  208. }
  209. }
  210. Ok((patches, local_patches, sync_patches, merge_patches))
  211. }
  212. async fn start(
  213. rpc_rv: async_channel::Receiver<String>,
  214. notify_sx: async_channel::Sender<Vec<Vec<(String, String)>>>,
  215. raft_sender: async_channel::Sender<Patch>,
  216. raft_receiver: async_channel::Receiver<Patch>,
  217. settings: DarkWikiSettings,
  218. ) -> DarkWikiResult<()> {
  219. loop {
  220. select! {
  221. command = rpc_rv.recv().fuse() => {
  222. let command = command.unwrap();
  223. match command.as_str() {
  224. "update" | "dry_run" => {
  225. let dry = command.as_str() == "dry_run";
  226. let (patches, local, sync, merge) = on_receive_update(&settings, dry)?;
  227. if !dry {
  228. for patch in patches {
  229. info!("Send a patch to Raft {:?}", patch);
  230. raft_sender.send(patch.clone()).await.map_err(Error::from)?;
  231. }
  232. }
  233. let local: Vec<(String, String)> =
  234. local.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  235. let sync: Vec<(String, String)> =
  236. sync.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  237. let merge: Vec<(String, String)> =
  238. merge.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  239. notify_sx.send(vec![local, sync, merge]).await.map_err(Error::from)?;
  240. }
  241. "log" => {
  242. // TODO
  243. notify_sx.send(vec![]).await.map_err(Error::from)?;
  244. }
  245. _ => {}
  246. }
  247. }
  248. patch = raft_receiver.recv().fuse() => {
  249. let patch = patch.map_err(Error::from)?;
  250. info!("Receive new patch from Raft {:?}", patch);
  251. on_receive_patch(&patch, &settings)?;
  252. }
  253. }
  254. }
  255. }
  256. async_daemonize!(realmain);
  257. async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
  258. let datastore_path = expand_path(&settings.datastore)?;
  259. let docs_path = expand_path(&settings.docs)?;
  260. create_dir_all(docs_path.clone())?;
  261. create_dir_all(datastore_path.join("local"))?;
  262. create_dir_all(datastore_path.join("sync"))?;
  263. let (rpc_sx, rpc_rv) = async_channel::unbounded::<String>();
  264. let (notify_sx, notify_rv) = async_channel::unbounded::<Vec<Vec<(String, String)>>>();
  265. //
  266. // RPC
  267. //
  268. let rpc_interface = Arc::new(JsonRpcInterface::new(rpc_sx, notify_rv));
  269. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface)).detach();
  270. //
  271. // Raft
  272. //
  273. let net_settings = settings.net;
  274. let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
  275. let datastore_raft = datastore_path.join("darkwiki.db");
  276. let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
  277. let mut raft = Raft::<Patch>::new(raft_settings, seen_net_msgs.clone())?;
  278. //
  279. // P2p setup
  280. //
  281. let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
  282. let p2p = net::P2p::new(net_settings.into()).await;
  283. let p2p = p2p.clone();
  284. let registry = p2p.protocol_registry();
  285. let raft_node_id = raft.id();
  286. registry
  287. .register(net::SESSION_ALL, move |channel, p2p| {
  288. let raft_node_id = raft_node_id.clone();
  289. let sender = p2p_send_channel.clone();
  290. let seen_net_msgs_cloned = seen_net_msgs.clone();
  291. async move {
  292. ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
  293. }
  294. })
  295. .await;
  296. p2p.clone().start(executor.clone()).await?;
  297. executor.spawn(p2p.clone().run(executor.clone())).detach();
  298. //
  299. // Darkwiki start
  300. //
  301. let darkwiki_settings = DarkWikiSettings { author: settings.author, datastore_path, docs_path };
  302. executor
  303. .spawn(start(rpc_rv, notify_sx, raft.sender(), raft.receiver(), darkwiki_settings))
  304. .detach();
  305. //
  306. // Waiting Exit signal
  307. //
  308. let (signal, shutdown) = async_channel::bounded::<()>(1);
  309. ctrlc_async::set_async_handler(async move {
  310. warn!(target: "darkwiki", "Catch exit signal");
  311. // cleaning up tasks running in the background
  312. if let Err(e) = signal.send(()).await {
  313. error!("Error on sending exit signal: {}", e);
  314. }
  315. })
  316. .unwrap();
  317. raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
  318. Ok(())
  319. }