main.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. let mut b_patch;
  151. // check for any changes found with local doc and darkwiki doc
  152. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
  153. // no changes found
  154. if local_patch.to_string() == edit {
  155. continue
  156. }
  157. // check the differences with LCS algorithm
  158. let lcs_ops = lcs(&local_patch.to_string(), edit);
  159. // add the change ops to the new patch
  160. for op in lcs_ops {
  161. new_patch.add_op(&op);
  162. }
  163. new_patch.base = local_patch.to_string();
  164. local_patches.push(new_patch.clone());
  165. // check if the same doc has received patch from the network
  166. if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
  167. if sync_patch.timestamp != local_patch.timestamp {
  168. sync_patches.push(sync_patch.clone());
  169. let sync_patch_t = new_patch.transform(&sync_patch);
  170. new_patch = new_patch.merge(&sync_patch_t);
  171. if !dry {
  172. save_file(&docs_path.join(doc_title), &new_patch.to_string())?;
  173. }
  174. merge_patches.push(new_patch.clone());
  175. }
  176. }
  177. b_patch = new_patch.clone();
  178. b_patch.base = "".to_string();
  179. } else {
  180. new_patch.base = edit.to_string();
  181. local_patches.push(new_patch.clone());
  182. b_patch = 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. patches.push(b_patch);
  189. }
  190. // check if a new patch received
  191. // and save the new changes in both local and darkwiki dirs
  192. let sync_files = read_dir(&sync_path).map_err(Error::from)?;
  193. for file in sync_files {
  194. let file_id = file.as_ref().unwrap().file_name();
  195. let file_id = file_id.to_str().unwrap();
  196. let file_path = sync_path.join(&file_id);
  197. let sync_patch: Patch = load_json_file(&file_path)?;
  198. if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&file_id)) {
  199. if local_patch.timestamp == sync_patch.timestamp {
  200. continue
  201. }
  202. }
  203. if !dry {
  204. save_file(&docs_path.join(&sync_patch.title), &sync_patch.to_string())?;
  205. save_json_file(&local_path.join(file_id), &sync_patch)?;
  206. }
  207. sync_patches.push(sync_patch);
  208. }
  209. Ok((patches, local_patches, sync_patches, merge_patches))
  210. }
  211. async fn start(
  212. rpc_rv: async_channel::Receiver<String>,
  213. notify_sx: async_channel::Sender<Vec<Vec<(String, String)>>>,
  214. raft_sender: async_channel::Sender<Patch>,
  215. raft_receiver: async_channel::Receiver<Patch>,
  216. settings: DarkWikiSettings,
  217. ) -> DarkWikiResult<()> {
  218. loop {
  219. select! {
  220. command = rpc_rv.recv().fuse() => {
  221. let command = command.unwrap();
  222. match command.as_str() {
  223. "update" | "dry_run" => {
  224. let dry = command.as_str() == "dry_run";
  225. let (patches, local, sync, merge) = on_receive_update(&settings, dry)?;
  226. if !dry {
  227. for patch in patches {
  228. info!("Send a patch to Raft {:?}", patch);
  229. raft_sender.send(patch.clone()).await.map_err(Error::from)?;
  230. }
  231. }
  232. let local: Vec<(String, String)> =
  233. local.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  234. let sync: Vec<(String, String)> =
  235. sync.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  236. let merge: Vec<(String, String)> =
  237. merge.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
  238. notify_sx.send(vec![local, sync, merge]).await.map_err(Error::from)?;
  239. }
  240. "log" => {
  241. // TODO
  242. notify_sx.send(vec![]).await.map_err(Error::from)?;
  243. }
  244. _ => {}
  245. }
  246. }
  247. patch = raft_receiver.recv().fuse() => {
  248. let patch = patch.map_err(Error::from)?;
  249. info!("Receive new patch from Raft {:?}", patch);
  250. on_receive_patch(&patch, &settings)?;
  251. }
  252. }
  253. }
  254. }
  255. async_daemonize!(realmain);
  256. async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
  257. let datastore_path = expand_path(&settings.datastore)?;
  258. let docs_path = expand_path(&settings.docs)?;
  259. create_dir_all(docs_path.clone())?;
  260. create_dir_all(datastore_path.join("local"))?;
  261. create_dir_all(datastore_path.join("sync"))?;
  262. let (rpc_sx, rpc_rv) = async_channel::unbounded::<String>();
  263. let (notify_sx, notify_rv) = async_channel::unbounded::<Vec<Vec<(String, String)>>>();
  264. //
  265. // RPC
  266. //
  267. let rpc_interface = Arc::new(JsonRpcInterface::new(rpc_sx, notify_rv));
  268. executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface)).detach();
  269. //
  270. // Raft
  271. //
  272. let net_settings = settings.net;
  273. let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
  274. let datastore_raft = datastore_path.join("darkwiki.db");
  275. let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
  276. let mut raft = Raft::<Patch>::new(raft_settings, seen_net_msgs.clone())?;
  277. //
  278. // P2p setup
  279. //
  280. let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
  281. let p2p = net::P2p::new(net_settings.into()).await;
  282. let p2p = p2p.clone();
  283. let registry = p2p.protocol_registry();
  284. let raft_node_id = raft.id();
  285. registry
  286. .register(net::SESSION_ALL, move |channel, p2p| {
  287. let raft_node_id = raft_node_id.clone();
  288. let sender = p2p_send_channel.clone();
  289. let seen_net_msgs_cloned = seen_net_msgs.clone();
  290. async move {
  291. ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
  292. }
  293. })
  294. .await;
  295. p2p.clone().start(executor.clone()).await?;
  296. executor.spawn(p2p.clone().run(executor.clone())).detach();
  297. //
  298. // Darkwiki start
  299. //
  300. let darkwiki_settings = DarkWikiSettings { author: settings.author, datastore_path, docs_path };
  301. executor
  302. .spawn(start(rpc_rv, notify_sx, raft.sender(), raft.receiver(), darkwiki_settings))
  303. .detach();
  304. //
  305. // Waiting Exit signal
  306. //
  307. let (signal, shutdown) = async_channel::bounded::<()>(1);
  308. ctrlc_async::set_async_handler(async move {
  309. warn!(target: "darkwiki", "Catch exit signal");
  310. // cleaning up tasks running in the background
  311. if let Err(e) = signal.send(()).await {
  312. error!("Error on sending exit signal: {}", e);
  313. }
  314. })
  315. .unwrap();
  316. raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
  317. Ok(())
  318. }