Просмотр исходного кода

darkwiki: Remove obsolete code.

parazyd 3 лет назад
Родитель
Сommit
2a32894eeb

+ 0 - 3
bin/darkwiki/README.md

@@ -1,3 +0,0 @@
-# Darkwiki 
-
-see [Darkfi Book](https://darkrenaissance.github.io/darkfi/misc/darkwiki.html) for the installation guide.

+ 0 - 30
bin/darkwiki/darkwiki-cli/Cargo.toml

@@ -1,30 +0,0 @@
-[package]
-name = "darkwiki"
-description = "CLI utility for interacting with darkwikid"
-version = "0.4.1"
-edition = "2021"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-license = "AGPL-3.0-only"
-homepage = "https://dark.fi"
-repository = "https://github.com/darkrenaissance/darkfi"
-
-[dependencies]
-darkfi = {path = "../../../", features = ["rpc"]}
-
-# Async
-smol = "1.3.0"
-async-std = {version = "1.12.0", features = ["attributes"]}
-async-trait = "0.1.72"
-async-channel = "1.9.0"
-futures = "0.3.28"
-
-# Misc
-log = "0.4.19"
-simplelog = "0.12.1"
-rand = "0.8.5"
-url = "2.4.0"
-
-# Encoding and parsing
-serde = {version = "1.0.174", features = ["derive"]}
-serde_json = "1.0.103"
-structopt = "0.3.26"

+ 0 - 139
bin/darkwiki/darkwiki-cli/src/main.rs

@@ -1,139 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use serde_json::json;
-use simplelog::{ColorChoice, TermLogger, TerminalMode};
-use structopt::StructOpt;
-use url::Url;
-
-use darkfi::{
-    rpc::{client::RpcClient, jsonrpc::JsonRequest},
-    util::cli::{get_log_config, get_log_level},
-    Result,
-};
-
-#[derive(Clone, Debug, StructOpt)]
-#[structopt(name = "darkwikiupdate")]
-struct Args {
-    #[structopt(subcommand)]
-    sub_command: ArgsSubCommand,
-    #[structopt(short, parse(from_occurrences))]
-    /// Increase verbosity (-vvv supported)
-    verbose: u8,
-    #[structopt(short, long, default_value = "tcp://127.0.0.1:24330")]
-    /// darkfid JSON-RPC endpoint
-    endpoint: Url,
-}
-
-#[derive(Debug, Clone, PartialEq, StructOpt)]
-enum ArgsSubCommand {
-    /// Publish local patches and merging received patches
-    Update {
-        #[structopt(long, short)]
-        /// Run without applying the changes
-        dry_run: bool,
-        /// Names of files to update (Note: Will update all the documents if left empty)
-        values: Vec<String>,
-    },
-    /// Show the history of patches  
-    Log {
-        /// Names of files to log (Note: Will show all the log if left empty)
-        values: Vec<String>,
-    },
-    /// Undo the local changes
-    Restore {
-        #[structopt(long, short)]
-        /// Run without applying the changes
-        dry_run: bool,
-        /// Names of files to restore (Note: Will restore all the documents if left empty)
-        values: Vec<String>,
-    },
-}
-
-fn print_patches(value: &Vec<serde_json::Value>) {
-    for res in value {
-        let res = res.as_array().unwrap();
-        let res: Vec<&str> = res.iter().map(|r| r.as_str().unwrap()).collect();
-        let (title, workspace, changes) = (res[0], res[1], res[2]);
-        println!("WORKSPACE: {} FILE: {}", workspace, title);
-        println!("{}", changes);
-        println!("----------------------------------");
-    }
-}
-
-#[async_std::main]
-async fn main() -> Result<()> {
-    let args = Args::from_args();
-
-    let log_level = get_log_level(args.verbose.into());
-    let log_config = get_log_config();
-    TermLogger::init(log_level, log_config, TerminalMode::Mixed, ColorChoice::Auto)?;
-
-    let rpc_client = RpcClient::new(args.endpoint).await?;
-
-    match args.sub_command {
-        ArgsSubCommand::Update { dry_run, values } => {
-            let req = JsonRequest::new("update", json!([dry_run, values]));
-
-            let result = rpc_client.request(req).await?;
-
-            let result = result.as_array().unwrap();
-            let local_patches = result[0].as_array().unwrap();
-            let sync_patches = result[1].as_array().unwrap();
-            let merge_patches = result[2].as_array().unwrap();
-
-            if !local_patches.is_empty() {
-                println!();
-                println!("PUBLISH LOCAL PATCHES:");
-                println!();
-                print_patches(local_patches);
-            }
-
-            if !sync_patches.is_empty() {
-                println!();
-                println!("RECEIVED PATCHES:");
-                println!();
-                print_patches(sync_patches);
-            }
-
-            if !merge_patches.is_empty() {
-                println!();
-                println!("MERGE:");
-                println!();
-                print_patches(merge_patches);
-            }
-        }
-        ArgsSubCommand::Restore { dry_run, values } => {
-            let req = JsonRequest::new("restore", json!([dry_run, values]));
-            let result = rpc_client.request(req).await?;
-
-            let result = result.as_array().unwrap();
-            let patches = result[0].as_array().unwrap();
-
-            if !patches.is_empty() {
-                println!();
-                println!("AFTER RESTORE:");
-                println!();
-                print_patches(patches);
-            }
-        }
-        _ => unimplemented!(),
-    }
-
-    rpc_client.close().await
-}

+ 0 - 34
bin/darkwiki/darkwikid/Cargo.toml

@@ -1,34 +0,0 @@
-[package]
-name = "darkwikid"
-version = "0.4.1"
-homepage = "https://dark.fi"
-description = "Reference daemon for a decentralized P2P wiki"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-repository = "https://github.com/darkrenaissance/darkfi"
-license = "AGPL-3.0-only"
-edition = "2021"
-
-[dependencies]
-async-std = "1.12.0"
-async-trait = "0.1.72"
-blake3 = "1.4.1"
-bs58 = "0.5.0"
-darkfi = {path = "../../../", features = ["raft", "rpc", "util"]}
-darkfi-serial = {path = "../../../src/serial"}
-dryoc = "0.5.0"
-easy-parallel = "3.3.0"
-futures = "0.3.28"
-lazy_static = "1.4.0"
-log = "0.4.19"
-serde = "1.0.174"
-serde_derive = "1.0.174"
-serde_json = "1.0.103"
-signal-hook-async-std = "0.2.2"
-signal-hook = "0.3.17"
-simplelog = "0.12.1"
-structopt = "0.3.26"
-structopt-toml = "0.5.1"
-smol = "1.3.0"
-toml = "0.7.6"
-unicode-segmentation = "1.10.1"
-url = "2.4.0"

+ 0 - 59
bin/darkwiki/darkwikid/darkwikid_config.toml

@@ -1,59 +0,0 @@
-## darkwiki configuration file
-##
-## Please make sure you go through all the settings so you can configure
-## your daemon properly.
-##
-## The default values are left commented. They can be overridden either by
-## uncommenting, or by using the commandline.
-
-# JSON-RPC endpoint listen URL, this is where the daemon's clients connect to. 
-# Usually, this should be listening on localhost, where you'll also be running
-# your clients that interface with the daemon. Running the JSON-RPC endpoint
-# publicly accessible is dangerous as it allows others to interact with it and
-# potentially destroy and/or steal your data!
-#rpc_listen = "tcp://localhost:24330"
-
-# Toplevel path to where you wish to store darkwiki files. This is where you'll
-# make edits to files and commit them.
-#docs = "~/darkwiki"
-
-# The nickname your darkwikid will use for your patches.
-#author = "Anonymous"
-
-# Workspaces, configured as an array, so instead of having multiple
-# keys, just append them in this list:
-# workspace = [
-#     "darkwiki_playground:Ar7GhqEPdc8dYWbmPwLaTfvtHGwaS9Ki2UmSJvCURisd",
-# ]
-
-# Network settings
-[net]
-# P2P accept addresses, set this to 0.0.0.0 and/or [::] to listen on all
-# available interfaces. Alternatively, add the IP addresses or domains you
-# want your daemon to listen to.
-#inbound = ["tls://0.0.0.0:24331"]
-
-# Outbound connection slots, the target number of peers your node will try
-# to connect to.
-outbound_connections = 8
-
-# P2P external addresses. If your node is reachable from the Internet, this
-# address will be advertised to the seed node(s) you connect to for bootstrap.
-# If your daemon isn't reachable by others, leave this commented.
-#external_addr = ["tls://your_ip_addr_or_domain:24331"]
-
-# Manual connections to peers. This is in addition to outbound_connections.
-#peers = ["tls://127.0.0.1:24331"]
-
-# Seed nodes to connect to
-seeds = ["tls://lilith0.dark.fi:24331", "tls://lilith1.dark.fi:24331"]
-
-# Prefered transports for outbound connections
-#transports = ["tls", "tcp"]
-
-# These are the default configuration for the P2P network
-#manual_attempt_limit = 0
-#seed_query_timeout_seconds = 8
-#connect_timeout_seconds = 10
-#channel_handshake_seconds = 4
-#channel_heartbeat_seconds = 10

+ 0 - 129
bin/darkwiki/darkwikid/src/jsonrpc.rs

@@ -1,129 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use async_trait::async_trait;
-use log::error;
-use serde_json::{json, Value};
-
-use darkfi::{
-    rpc::{
-        jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
-        server::RequestHandler,
-    },
-    Error,
-};
-
-use crate::Patch;
-
-pub struct JsonRpcInterface {
-    sender: smol::channel::Sender<(String, bool, Vec<String>)>,
-    receiver: smol::channel::Receiver<Vec<Vec<Patch>>>,
-}
-
-#[async_trait]
-impl RequestHandler for JsonRpcInterface {
-    async fn handle_request(&self, req: JsonRequest) -> JsonResult {
-        if !req.params.is_array() {
-            return JsonError::new(ErrorCode::InvalidParams, None, req.id).into()
-        }
-
-        let params = req.params.as_array().unwrap();
-
-        let rep = match req.method.as_str() {
-            Some("update") => self.update(req.id, params).await,
-            Some("restore") => self.restore(req.id, params).await,
-            Some("log") => self.log(req.id, params).await,
-            Some(_) | None => return JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
-        };
-
-        rep
-    }
-}
-
-fn patch_to_tuple(p: &Patch, colorize: bool) -> (String, String, String) {
-    (p.path.to_owned(), p.workspace.to_owned(), if colorize { p.colorize() } else { p.to_string() })
-}
-
-fn printable_patches(
-    patches: Vec<Vec<Patch>>,
-    colorize: bool,
-) -> Vec<Vec<(String, String, String)>> {
-    let mut response = vec![];
-    for ps in patches {
-        response.push(ps.iter().map(|p| patch_to_tuple(p, colorize)).collect())
-    }
-    response
-}
-
-impl JsonRpcInterface {
-    pub fn new(
-        sender: smol::channel::Sender<(String, bool, Vec<String>)>,
-        receiver: smol::channel::Receiver<Vec<Vec<Patch>>>,
-    ) -> Self {
-        Self { sender, receiver }
-    }
-
-    // RPCAPI:
-    // Update files
-    // --> {"jsonrpc": "2.0", "method": "update", "params": [], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": [[(String, String)]], "id": 1}
-    async fn update(&self, id: Value, params: &[Value]) -> JsonResult {
-        let dry = params[0].as_bool().unwrap();
-        let files: Vec<String> =
-            params[1].as_array().unwrap().iter().map(|f| f.as_str().unwrap().to_string()).collect();
-        let res = self.sender.send(("update".into(), dry, files)).await.map_err(Error::from);
-
-        if let Err(e) = res {
-            error!("Failed to update: {}", e);
-            return JsonError::new(ErrorCode::InternalError, None, id).into()
-        }
-
-        let response = self.receiver.recv().await.unwrap();
-        let response = printable_patches(response, true);
-        JsonResponse::new(json!(response), id).into()
-    }
-
-    // RPCAPI:
-    // Undo the local changes
-    // --> {"jsonrpc": "2.0", "method": "restore", "params": [dry, files], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": [String, ..], "id": 1}
-    async fn restore(&self, id: Value, params: &[Value]) -> JsonResult {
-        let dry = params[0].as_bool().unwrap();
-        let files: Vec<String> =
-            params[1].as_array().unwrap().iter().map(|f| f.as_str().unwrap().to_string()).collect();
-
-        let res = self.sender.send(("restore".into(), dry, files)).await.map_err(Error::from);
-
-        if let Err(e) = res {
-            error!("Failed to restore: {}", e);
-            return JsonError::new(ErrorCode::InternalError, None, id).into()
-        }
-
-        let response = self.receiver.recv().await.unwrap();
-        let response = printable_patches(response, false);
-        JsonResponse::new(json!(response), id).into()
-    }
-
-    // RPCAPI:
-    // Show all patches
-    // --> {"jsonrpc": "2.0", "method": "log", "params": [dry, files], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": [[(String, String)]], "id": 1}
-    async fn log(&self, id: Value, _params: &[Value]) -> JsonResult {
-        JsonResponse::new(json!(true), id).into()
-    }
-}

+ 0 - 120
bin/darkwiki/darkwikid/src/lcs.rs

@@ -1,120 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use crate::{patch::OpMethod, util::str_to_chars};
-
-pub struct Lcs<'a> {
-    a: Vec<&'a str>,
-    b: Vec<&'a str>,
-    lengths: Vec<Vec<u64>>,
-}
-
-impl<'a> Lcs<'a> {
-    pub fn new(a: &'a str, b: &'a str) -> Self {
-        let a: Vec<_> = str_to_chars(a);
-        let b: Vec<_> = str_to_chars(b);
-        let (na, nb) = (a.len(), b.len());
-
-        let mut lengths = vec![vec![0; nb + 1]; na + 1];
-
-        for (i, ci) in a.iter().enumerate() {
-            for (j, cj) in b.iter().enumerate() {
-                lengths[i + 1][j + 1] = if ci == cj {
-                    lengths[i][j] + 1
-                } else {
-                    lengths[i][j + 1].max(lengths[i + 1][j])
-                }
-            }
-        }
-
-        Self { a, b, lengths }
-    }
-
-    fn op(&self, ops: &mut Vec<OpMethod>, i: usize, j: usize) {
-        if i == 0 && j == 0 {
-            return
-        }
-
-        if i == 0 {
-            ops.push(OpMethod::Insert(self.b[j - 1].to_string()));
-            self.op(ops, i, j - 1);
-        } else if j == 0 {
-            ops.push(OpMethod::Delete((1) as _));
-            self.op(ops, i - 1, j);
-        } else if self.a[i - 1] == self.b[j - 1] {
-            ops.push(OpMethod::Retain((1) as _));
-            self.op(ops, i - 1, j - 1);
-        } else if self.lengths[i - 1][j] > self.lengths[i][j - 1] {
-            ops.push(OpMethod::Delete((1) as _));
-            self.op(ops, i - 1, j);
-        } else {
-            ops.push(OpMethod::Insert(self.b[j - 1].to_string()));
-            self.op(ops, i, j - 1);
-        }
-    }
-
-    pub fn ops(&self) -> Vec<OpMethod> {
-        let mut ops = vec![];
-        self.op(&mut ops, self.a.len(), self.b.len());
-        ops.reverse();
-        ops
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_lcs() {
-        let lcs = Lcs::new("hello", "test hello");
-        assert_eq!(
-            lcs.ops(),
-            vec![
-                OpMethod::Insert("t".into()),
-                OpMethod::Insert("e".into()),
-                OpMethod::Insert("s".into()),
-                OpMethod::Insert("t".into()),
-                OpMethod::Insert(" ".into()),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-            ]
-        );
-
-        let lcs = Lcs::new("hello world", "hello");
-        assert_eq!(
-            lcs.ops(),
-            vec![
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Retain(1),
-                OpMethod::Delete(1),
-                OpMethod::Delete(1),
-                OpMethod::Delete(1),
-                OpMethod::Retain(1),
-                OpMethod::Delete(1),
-                OpMethod::Delete(1),
-                OpMethod::Delete(1),
-            ]
-        );
-    }
-}

+ 0 - 662
bin/darkwiki/darkwikid/src/main.rs

@@ -1,662 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{
-    collections::HashMap,
-    fs::{create_dir_all, read_dir, remove_file},
-    io::stdin,
-    path::{Path, PathBuf},
-    process::exit,
-};
-
-use async_std::{
-    stream::StreamExt,
-    sync::{Arc, Mutex, RwLock},
-    task,
-};
-use dryoc::classic::crypto_secretbox::{crypto_secretbox_keygen, Key};
-use futures::{select, FutureExt};
-use lazy_static::lazy_static;
-use log::{debug, error, info, warn};
-use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
-use signal_hook_async_std::Signals;
-use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
-use url::Url;
-
-use darkfi::{
-    async_daemonize, cli_desc, net,
-    raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
-    rpc::server::listen_and_serve,
-    util::{
-        file::{load_file, load_json_file, save_file, save_json_file},
-        path::{expand_path, get_config_path},
-    },
-    Result,
-};
-
-mod jsonrpc;
-use jsonrpc::JsonRpcInterface;
-mod lcs;
-use lcs::Lcs;
-mod patch;
-use patch::{EncryptedPatch, OpMethod, Patch};
-mod util;
-use util::{decrypt_patch, encrypt_patch, get_docs_paths, parse_workspaces, path_to_id};
-
-type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
-
-lazy_static! {
-    /// This is where we hold our workspaces, so we are also able to refresh them on SIGHUP.
-    static ref WORKSPACES: RwLock<HashMap<String, Key>> = RwLock::new(HashMap::new());
-}
-
-pub const CONFIG_FILE: &str = "darkwikid_config.toml";
-pub const CONFIG_FILE_CONTENTS: &str = include_str!("../darkwikid_config.toml");
-
-const SYNC_ID_PATH: &str = "sync";
-const LOCAL_ID_PATH: &str = "local";
-
-#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
-#[serde(default)]
-#[structopt(name = "darkwikid", about = cli_desc!())]
-struct Args {
-    /// Increase verbosity (-vvv supported)
-    #[structopt(short, parse(from_occurrences))]
-    verbose: u8,
-
-    /// Configuration file to use
-    #[structopt(short, long)]
-    config: Option<String>,
-
-    /// Workspace configuration (repeatable flag)
-    #[structopt(short, long)]
-    workspace: Vec<String>,
-
-    /// Path where to store wiki's files
-    #[structopt(short, long, default_value = "~/darkwiki")]
-    docs: String,
-
-    /// Sets author's name for patches
-    #[structopt(long, default_value = "Anonymous")]
-    author: String,
-
-    /// Generate a new secret for a workspace
-    #[structopt(long)]
-    gen_secret: bool,
-
-    /// JSON-RPC listen URL
-    #[structopt(long, default_value = "tcp://localhost:24330")]
-    rpc_listen: Url,
-
-    /// Network settings
-    #[structopt(flatten)]
-    net: net::settings::SettingsOpt,
-}
-
-/// Settings struct used to hold some metadata for DarkWiki
-struct DarkWikiSettings {
-    author: String,
-    docs_path: PathBuf,
-    store_path: PathBuf,
-}
-
-/// DarkWiki object
-struct DarkWiki {
-    settings: DarkWikiSettings,
-    #[allow(clippy::type_complexity)]
-    rpc: (
-        smol::channel::Sender<Vec<Vec<Patch>>>,
-        smol::channel::Receiver<(String, bool, Vec<String>)>,
-    ),
-    raft: (smol::channel::Sender<EncryptedPatch>, smol::channel::Receiver<EncryptedPatch>),
-}
-
-impl DarkWiki {
-    async fn start(&self) -> Result<()> {
-        loop {
-            select! {
-                val = self.rpc.1.recv().fuse() => {
-                    let (cmd, dry, files) = match val {
-                        Ok(v) => v,
-                        Err(e) => {
-                            error!("Failed unwrapping val received from RPC: {}", e);
-                            continue
-                        }
-                    };
-
-                    match cmd.as_str() {
-                        "update" => {
-                            if let Err(e) = self.on_receive_update(dry, files).await {
-                                error!("on_receive_update returned error: {}", e);
-                                continue
-                            }
-                        }
-
-                        "restore" => {
-                            if let Err(e) = self.on_receive_restore(dry, files).await {
-                                error!("on_receive_restore returned error: {}", e);
-                                continue
-                            }
-                        }
-
-                        x => {
-                            warn!("Received unsupported command: {}", x);
-                            continue
-                        }
-                    }
-                }
-
-                patch = self.raft.1.recv().fuse() => {
-                    let patch = match patch {
-                        Ok(v) => v,
-                        Err(e) => {
-                            error!("Failed unwrapping patch received from raft: {}", e);
-                            continue
-                        }
-                    };
-
-                    for (workspace, key) in WORKSPACES.read().await.iter() {
-                        if let Ok(mut patch) = decrypt_patch(&patch, key) {
-                            info!("[{}] Receive a {:?}", workspace, patch);
-                            patch.workspace = workspace.clone();
-                            if let Err(e) = self.on_receive_patch(&patch) {
-                                error!("on_receive_patch returned error: {}", e);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn on_receive_patch(&self, received_patch: &Patch) -> Result<()> {
-        let sync_id_path = self.settings.store_path.join(SYNC_ID_PATH).join(&received_patch.id);
-        let local_id_path = self.settings.store_path.join(LOCAL_ID_PATH).join(&received_patch.id);
-
-        if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
-            if sync_patch.timestamp == received_patch.timestamp {
-                return Ok(())
-            }
-
-            if let Ok(local_patch) = load_json_file::<Patch>(&local_id_path) {
-                if local_patch.timestamp == sync_patch.timestamp {
-                    sync_patch.base = local_patch.to_string();
-                    sync_patch.set_ops(received_patch.ops());
-                } else {
-                    sync_patch.extend_ops(received_patch.ops());
-                }
-            }
-
-            sync_patch.timestamp = received_patch.timestamp;
-            sync_patch.author = received_patch.author.clone();
-            save_json_file::<Patch>(&sync_id_path, &sync_patch)?;
-        } else if !received_patch.base.is_empty() {
-            save_json_file::<Patch>(&sync_id_path, received_patch)?;
-        }
-
-        Ok(())
-    }
-
-    async fn on_receive_update(&self, dry: bool, files: Vec<String>) -> Result<()> {
-        let (mut local, mut sync, mut merge) = (vec![], vec![], vec![]);
-
-        for (workspace, key) in WORKSPACES.read().await.iter() {
-            let (patches, l, s, m) = self.update(
-                dry,
-                &self.settings.docs_path.join(workspace),
-                files.clone(),
-                workspace,
-            )?;
-
-            local.extend(l);
-            sync.extend(s);
-            merge.extend(m);
-
-            if !dry {
-                for patch in patches {
-                    info!("Send a {:?}", patch);
-                    let encrypt_patch = encrypt_patch(&patch, key)?;
-                    self.raft.0.send(encrypt_patch).await?;
-                }
-            }
-        }
-
-        self.rpc.0.send(vec![local, sync, merge]).await?;
-        Ok(())
-    }
-
-    async fn on_receive_restore(&self, dry: bool, filenames: Vec<String>) -> Result<()> {
-        let mut patches = vec![];
-
-        for (workspace, _) in WORKSPACES.read().await.iter() {
-            patches.extend(self.restore(
-                dry,
-                &self.settings.docs_path.join(workspace),
-                &filenames,
-                workspace,
-            )?);
-        }
-
-        self.rpc.0.send(vec![patches]).await?;
-        Ok(())
-    }
-
-    fn restore(
-        &self,
-        dry: bool,
-        docs_path: &Path,
-        filenames: &[String],
-        workspace: &str,
-    ) -> Result<Vec<Patch>> {
-        let local_path = self.settings.store_path.join(LOCAL_ID_PATH);
-        let mut patches = vec![];
-
-        let local_files = read_dir(&local_path)?;
-        for file in local_files {
-            let file_id = file?.file_name();
-            let file_path = local_path.join(&file_id);
-            let local_patch: Patch = load_json_file(&file_path)?;
-
-            if local_patch.workspace != workspace {
-                continue
-            }
-
-            // TODO: FIXME: Simplify this logic, what is this? Add comments.
-            if !filenames.is_empty() && !filenames.contains(&local_patch.path.to_string()) {
-                continue
-            }
-
-            if let Ok(doc) = load_file(&docs_path.join(&local_patch.path)) {
-                if local_patch.to_string() == doc {
-                    continue
-                }
-            }
-
-            if !dry {
-                self.save_doc(&local_patch.path, &local_patch.to_string(), workspace)?;
-            }
-
-            patches.push(local_patch);
-        }
-
-        Ok(patches)
-    }
-
-    // TODO: Add debug/info statements and refactor this function, there's too many things going on here.
-    fn update(
-        &self,
-        dry: bool,
-        docs_path: &Path,
-        filenames: Vec<String>,
-        workspace: &str,
-    ) -> Result<Patches> {
-        let (mut patches, mut local_patches, mut sync_patches, mut merge_patches) =
-            (vec![], vec![], vec![], vec![]);
-
-        let local_path = self.settings.store_path.join(LOCAL_ID_PATH);
-        let sync_path = self.settings.store_path.join(SYNC_ID_PATH);
-
-        // Save and compare docs in darkwiki and local dirs, then
-        // merge with sync patches if any have been received.
-        let mut docs = vec![];
-        get_docs_paths(&mut docs, docs_path, None)?;
-        for doc in docs {
-            let doc_path = doc.to_str().unwrap();
-
-            // FIXME: IDGI
-            if !filenames.is_empty() && !filenames.contains(&doc_path.to_string()) {
-                continue
-            }
-
-            // Load doc content
-            let edit = load_file(&docs_path.join(doc_path))?;
-            if edit.is_empty() {
-                continue
-            }
-
-            let doc_id = path_to_id(doc_path, workspace);
-
-            // Create new patch
-            let mut new_patch = Patch::new(doc_path, &doc_id, &self.settings.author, workspace);
-
-            // Check for any changes found with local doc and darkwiki doc
-            if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
-                // No changes found
-                if local_patch.to_string() == edit {
-                    continue
-                }
-
-                // Check the differences with LCS algorithm
-                let local_patch_str = local_patch.to_string();
-                let lcs = Lcs::new(&local_patch_str, &edit);
-                let lcs_ops = lcs.ops();
-
-                // Add the change ops to the new patch
-                for op in lcs_ops {
-                    new_patch.add_op(&op);
-                }
-
-                new_patch.base = local_patch.to_string();
-                local_patches.push(new_patch.clone());
-
-                let mut b_patch = new_patch.clone();
-                b_patch.base = "".to_string();
-                patches.push(b_patch);
-
-                // Check if the same doc has received a patch from the network
-                if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
-                    if !Self::is_delete_patch(&sync_patch) {
-                        if sync_patch.timestamp != local_patch.timestamp {
-                            sync_patches.push(sync_patch.clone());
-
-                            let sync_patch_t = new_patch.transform(&sync_patch);
-                            new_patch = new_patch.merge(&sync_patch_t);
-                            if !dry {
-                                self.save_doc(doc_path, &new_patch.to_string(), workspace)?;
-                            }
-                            merge_patches.push(new_patch.clone());
-                        }
-                    } else {
-                        merge_patches.push(sync_patch);
-                        patches = vec![];
-                    }
-                }
-            } else {
-                new_patch.base = edit.to_string();
-                local_patches.push(new_patch.clone());
-                patches.push(new_patch.clone());
-            };
-
-            if !dry {
-                save_json_file(&local_path.join(&doc_id), &new_patch)?;
-                save_json_file(&sync_path.join(&doc_id), &new_patch)?;
-            }
-        }
-
-        // Check if a new patch is received and save the new changes
-        // in both local and darkwiki dirs.
-        let sync_files = read_dir(&sync_path)?;
-        for file in sync_files {
-            let file_id = file?.file_name();
-            let file_path = sync_path.join(&file_id);
-            let sync_patch: Patch = load_json_file(&file_path)?;
-
-            if sync_patch.workspace != workspace {
-                continue
-            }
-
-            if Self::is_delete_patch(&sync_patch) {
-                if local_path.join(&sync_patch.id).exists() {
-                    sync_patches.push(sync_patch.clone());
-                }
-
-                if !dry {
-                    remove_file(docs_path.join(&sync_patch.path))?;
-                    remove_file(local_path.join(&sync_patch.id))?;
-                    remove_file(file_path)?;
-                }
-
-                continue
-            }
-
-            if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&file_id)) {
-                if local_patch.timestamp == sync_patch.timestamp {
-                    continue
-                }
-            }
-
-            // TODO: FIXME: IDGI AGAIN, HALP
-            if !filenames.is_empty() && !filenames.contains(&sync_patch.path.to_string()) {
-                continue
-            }
-
-            if !dry {
-                self.save_doc(&sync_patch.path, &sync_patch.to_string(), workspace)?;
-                save_json_file(&local_path.join(file_id), &sync_patch)?;
-            }
-
-            if !sync_patches.contains(&sync_patch) {
-                sync_patches.push(sync_patch);
-            }
-        }
-
-        // Check if any doc is removed from darkwiki filesystem.
-        let local_files = read_dir(&local_path)?;
-        for file in local_files {
-            let file_id = file?.file_name();
-            let file_path = local_path.join(&file_id);
-            let local_patch: Patch = load_json_file(&file_path)?;
-
-            if local_patch.workspace != workspace {
-                continue
-            }
-
-            // TODO: FIXME: Is it just supposed to check that filenames doesn't contain the local_patch?
-            if !filenames.is_empty() && !filenames.contains(&local_patch.path.to_string()) {
-                continue
-            }
-
-            if !docs_path.join(&local_patch.path).exists() {
-                let mut new_patch = Patch::new(
-                    &local_patch.path,
-                    &local_patch.id,
-                    &self.settings.author,
-                    &local_patch.workspace,
-                );
-                new_patch.add_op(&OpMethod::Delete(local_patch.to_string().len() as u64));
-                patches.push(new_patch.clone());
-
-                new_patch.base = local_patch.base;
-                local_patches.push(new_patch);
-
-                if !dry {
-                    remove_file(file_path)?;
-                }
-            }
-        }
-
-        Ok((patches, local_patches, sync_patches, merge_patches))
-    }
-
-    fn save_doc(&self, path: &str, edit: &str, workspace: &str) -> Result<()> {
-        let path = self.settings.docs_path.join(workspace).join(path);
-        if let Some(p) = path.parent() {
-            if !p.exists() && !p.to_str().unwrap().is_empty() {
-                create_dir_all(p)?;
-            }
-        }
-        save_file(&path, edit)
-    }
-
-    fn is_delete_patch(patch: &Patch) -> bool {
-        if patch.ops().0.len() != 1 {
-            return false
-        }
-
-        if let OpMethod::Delete(d) = patch.ops().0[0] {
-            if patch.base.len() as u64 == d {
-                return true
-            }
-        }
-
-        false
-    }
-}
-
-async fn handle_signals(
-    mut signals: Signals,
-    cfg_path: PathBuf,
-    term_tx: smol::channel::Sender<()>,
-) {
-    debug!("Started signal handler");
-    while let Some(signal) = signals.next().await {
-        match signal {
-            SIGHUP => {
-                info!("Caught SIGHUP");
-                let toml_contents = match std::fs::read_to_string(cfg_path.clone()) {
-                    Ok(v) => v,
-                    Err(e) => {
-                        error!("Couldn't load configuration file: {}", e);
-                        continue
-                    }
-                };
-
-                *WORKSPACES.write().await = parse_workspaces(&toml_contents);
-                info!("Reloaded workspaces");
-            }
-
-            SIGTERM | SIGINT | SIGQUIT => {
-                term_tx.send(()).await.unwrap();
-            }
-
-            _ => unreachable!(),
-        }
-    }
-}
-
-async_daemonize!(realmain);
-async fn realmain(args: Args, executor: Arc<smol::Executor<'_>>) -> Result<()> {
-    let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
-    let docs_path = expand_path(&args.docs)?;
-    let store_path = expand_path(docs_path.join(".log").to_str().unwrap())?;
-
-    create_dir_all(docs_path.clone())?;
-    create_dir_all(store_path.clone())?;
-    create_dir_all(store_path.join(LOCAL_ID_PATH))?;
-    create_dir_all(store_path.join(SYNC_ID_PATH))?;
-
-    if args.gen_secret {
-        eprintln!("Generating a new workspace");
-        loop {
-            eprint!("Input the name for the new workspace (use ascii chars): ");
-            let mut workspace = String::new();
-            stdin().read_line(&mut workspace)?;
-            // Non-exhaustive
-            let workspace =
-                workspace.replace(['\t', '\r', ' ', '/', '\\', '\'', '&', '~', ':'], "_");
-
-            if workspace.is_empty() || workspace.len() < 3 {
-                eprintln!("Error: Workspace name is empty or less than 3 characters. Try again.");
-                continue
-            }
-
-            let secret = bs58::encode(crypto_secretbox_keygen()).into_string();
-            create_dir_all(docs_path.join(workspace.clone()))?;
-
-            println!("Created workspace: {}:{}", workspace, secret);
-            eprintln!("Please add it to the config file.");
-            return Ok(())
-        }
-    }
-
-    // Signal handling for config reload and graceful termination.
-    let signals = Signals::new([SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
-    let handle = signals.handle();
-    let (term_tx, term_rx) = smol::channel::bounded::<()>(1);
-    let signals_task = task::spawn(handle_signals(signals, cfg_path.clone(), term_tx));
-    info!("Set up signal handling");
-
-    {
-        info!("Parsing configuration file for workspaces");
-        let toml_contents = std::fs::read_to_string(cfg_path.clone())?;
-        *WORKSPACES.write().await = parse_workspaces(&toml_contents);
-        if WORKSPACES.read().await.is_empty() {
-            eprintln!("Please add atleast one workspace to the config file.");
-            eprintln!("Run \"$ darkwikid --gen-secret\" to create a new workspace.");
-            exit(1);
-        }
-    }
-
-    let (rpc_tx, rpc_rx) = smol::channel::unbounded::<(String, bool, Vec<String>)>();
-    let (notify_tx, notify_rx) = smol::channel::unbounded::<Vec<Vec<Patch>>>();
-
-    // ===============
-    // JSON-RPC server
-    // ===============
-    let rpc_iface = Arc::new(JsonRpcInterface::new(rpc_tx, notify_rx));
-    let _ex = executor.clone();
-    executor.spawn(listen_and_serve(args.rpc_listen, rpc_iface, _ex)).detach();
-
-    // ====
-    // Raft
-    // ====
-    let seen_net_msgs = Arc::new(Mutex::new(HashMap::new()));
-    let store_raft = store_path.join("darkwiki.db");
-    let raft_settings = RaftSettings { datastore_path: store_raft, ..RaftSettings::default() };
-    // FIXME: This is a bad design, and needs a proper rework.
-    let raft =
-        Arc::new(Mutex::new(Raft::<EncryptedPatch>::new(raft_settings, seen_net_msgs.clone())?));
-
-    // =========
-    // P2P setup
-    // =========
-    let mut net_settings = args.net.clone();
-    net_settings.app_version = Some(option_env!("CARGO_PKG_VERSION").unwrap_or("").to_string());
-    let (p2p_tx, p2p_rx) = smol::channel::unbounded::<NetMsg>();
-    let p2p = net::P2p::new(net_settings.into()).await;
-    let registry = p2p.protocol_registry();
-
-    let raft_node_id = raft.lock().await.id();
-    registry.register(net::SESSION_ALL, move | channel, p2p| {
-        let raft_node_id = raft_node_id.clone();
-        let sender = p2p_tx.clone();
-        let seen_net_msgs = seen_net_msgs.clone();
-        async move {
-            ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs).await
-        }
-    }).await;
-
-    p2p.clone().start(executor.clone()).await?;
-    executor.spawn(p2p.clone().run(executor.clone())).detach();
-
-    // ==============
-    // Darkwiki start
-    // ==============
-    let raft_tx = raft.lock().await.sender();
-    let raft_rx = raft.lock().await.receiver();
-    executor
-        .spawn(async move {
-            let settings = DarkWikiSettings { author: args.author, store_path, docs_path };
-            let dw = DarkWiki { settings, raft: (raft_tx, raft_rx), rpc: (notify_tx, rpc_rx) };
-            dw.start().await.unwrap();
-        })
-        .detach();
-
-    let (raft_term_tx, raft_term_rx) = smol::channel::bounded::<()>(1);
-    let _p2p = p2p.clone();
-    let _ex = executor.clone();
-    executor
-        .spawn(async move { raft.lock().await.run(_p2p, p2p_rx, _ex, raft_term_rx).await.unwrap() })
-        .detach();
-
-    // Wait for termination signal
-    term_rx.recv().await?;
-    eprint!("\r");
-    info!("Caught termination signal, cleaning up and exiting...");
-    handle.close();
-    signals_task.await;
-
-    info!("Stopping Raft...");
-    raft_term_tx.send(()).await.unwrap();
-
-    info!("Stopping P2P network...");
-    p2p.stop().await;
-
-    info!("Bye.");
-    Ok(())
-}

+ 0 - 624
bin/darkwiki/darkwikid/src/patch.rs

@@ -1,624 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{cmp::Ordering, io};
-
-use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt};
-use dryoc::constants::CRYPTO_SECRETBOX_NONCEBYTES;
-use serde::{Deserialize, Serialize};
-
-use darkfi::util::{
-    cli::{fg_green, fg_red},
-    time::Timestamp,
-};
-
-use crate::util::str_to_chars;
-
-#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
-pub enum OpMethod {
-    Delete(u64),
-    Insert(String),
-    Retain(u64),
-}
-
-#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
-pub struct OpMethods(pub Vec<OpMethod>);
-
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-pub struct EncryptedPatch {
-    pub nonce: [u8; CRYPTO_SECRETBOX_NONCEBYTES],
-    pub ciphertext: Vec<u8>,
-}
-
-#[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)]
-pub struct Patch {
-    pub path: String,
-    pub author: String,
-    pub id: String,
-    pub base: String,
-    pub timestamp: Timestamp,
-    pub workspace: String,
-    ops: OpMethods,
-}
-
-impl std::string::ToString for Patch {
-    fn to_string(&self) -> String {
-        if self.ops.0.is_empty() {
-            return self.base.clone()
-        }
-
-        let mut st = vec![];
-        st.extend(str_to_chars(&self.base));
-        let st = &mut st.iter();
-
-        let mut new_st: Vec<&str> = vec![];
-
-        for op in self.ops.0.iter() {
-            match op {
-                OpMethod::Retain(n) => {
-                    for c in st.take(*n as usize) {
-                        new_st.push(c);
-                    }
-                }
-                OpMethod::Delete(n) => {
-                    for _ in 0..*n {
-                        st.next();
-                    }
-                }
-                OpMethod::Insert(insert) => {
-                    let chars = str_to_chars(insert);
-                    new_st.extend(chars);
-                }
-            }
-        }
-
-        new_st.join("")
-    }
-}
-
-impl Patch {
-    pub fn new(path: &str, id: &str, author: &str, workspace: &str) -> Self {
-        Self {
-            path: path.to_string(),
-            id: id.to_string(),
-            ops: OpMethods(vec![]),
-            base: String::new(),
-            workspace: workspace.to_string(),
-            author: author.to_string(),
-            timestamp: Timestamp::current_time(),
-        }
-    }
-
-    pub fn add_op(&mut self, method: &OpMethod) {
-        match method {
-            OpMethod::Delete(n) => {
-                if *n == 0 {
-                    return
-                }
-
-                if let Some(OpMethod::Delete(i)) = self.ops.0.last_mut() {
-                    *i += n;
-                } else {
-                    self.ops.0.push(method.to_owned());
-                }
-            }
-            OpMethod::Insert(insert) => {
-                if insert.is_empty() {
-                    return
-                }
-
-                if let Some(OpMethod::Insert(s)) = self.ops.0.last_mut() {
-                    *s += insert;
-                } else {
-                    self.ops.0.push(OpMethod::Insert(insert.to_owned()));
-                }
-            }
-            OpMethod::Retain(n) => {
-                if *n == 0 {
-                    return
-                }
-
-                if let Some(OpMethod::Retain(i)) = self.ops.0.last_mut() {
-                    *i += n;
-                } else {
-                    self.ops.0.push(method.to_owned());
-                }
-            }
-        }
-    }
-
-    fn insert(&mut self, st: &str) {
-        self.add_op(&OpMethod::Insert(st.into()));
-    }
-
-    fn retain(&mut self, n: u64) {
-        self.add_op(&OpMethod::Retain(n));
-    }
-
-    fn delete(&mut self, n: u64) {
-        self.add_op(&OpMethod::Delete(n));
-    }
-
-    pub fn set_ops(&mut self, ops: OpMethods) {
-        self.ops = ops;
-    }
-
-    pub fn extend_ops(&mut self, ops: OpMethods) {
-        self.ops.0.extend(ops.0);
-    }
-
-    pub fn ops(&self) -> OpMethods {
-        self.ops.clone()
-    }
-
-    //
-    // these two functions are imported from this library
-    // https://github.com/spebern/operational-transform-rs
-    // with some major modification
-    //
-    // TODO need more work to get better performance with iterators
-    pub fn transform(&self, other: &Self) -> Self {
-        let mut new_patch = Self::new(&self.path, &self.id, &self.author, "");
-        new_patch.base = self.base.clone();
-
-        let mut ops1 = self.ops.0.iter().cloned();
-        let mut ops2 = other.ops.0.iter().cloned();
-
-        let mut op1 = ops1.next();
-        let mut op2 = ops2.next();
-        loop {
-            match (&op1, &op2) {
-                (None, None) => break,
-                (None, Some(op)) => {
-                    new_patch.add_op(op);
-                    op2 = ops2.next();
-                    continue
-                }
-                (Some(op), None) => {
-                    new_patch.add_op(op);
-                    op1 = ops1.next();
-                    continue
-                }
-                _ => {}
-            }
-
-            match (op1.as_ref().unwrap(), op2.as_ref().unwrap()) {
-                (OpMethod::Insert(s), _) => {
-                    new_patch.retain(str_to_chars(s).len() as _);
-                    op1 = ops1.next();
-                }
-                (_, OpMethod::Insert(s)) => {
-                    new_patch.insert(s);
-                    op2 = ops2.next();
-                }
-                (OpMethod::Retain(i), OpMethod::Retain(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        new_patch.retain(*i);
-                        op2 = Some(OpMethod::Retain(j - *i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        new_patch.retain(*j);
-                        op1 = Some(OpMethod::Retain(i - j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        new_patch.retain(*i);
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-                (OpMethod::Delete(i), OpMethod::Delete(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        op2 = Some(OpMethod::Delete(j - *i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        op1 = Some(OpMethod::Delete(i - j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-                (OpMethod::Delete(i), OpMethod::Retain(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        op2 = Some(OpMethod::Retain(j - *i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        op1 = Some(OpMethod::Delete(i - j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-                (OpMethod::Retain(i), OpMethod::Delete(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        new_patch.delete(*i);
-                        op2 = Some(OpMethod::Delete(j - i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        new_patch.delete(*j);
-                        op1 = Some(OpMethod::Retain(i - j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        new_patch.delete(*i);
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-            }
-        }
-
-        new_patch
-    }
-
-    // TODO need more work to get better performance with iterators
-    pub fn merge(&mut self, other: &Self) -> Self {
-        let ops1 = self.ops.0.clone();
-        let mut ops1 = ops1.iter().cloned();
-        let mut ops2 = other.ops.0.iter().cloned();
-
-        let mut new_patch = Self::new(&self.path, &self.id, &self.author, "");
-        new_patch.base = self.base.clone();
-
-        let mut op1 = ops1.next();
-        let mut op2 = ops2.next();
-
-        loop {
-            match (&op1, &op2) {
-                (None, None) => break,
-                (None, Some(op)) => {
-                    new_patch.add_op(op);
-                    op2 = ops2.next();
-                    continue
-                }
-                (Some(op), None) => {
-                    new_patch.add_op(op);
-                    op1 = ops1.next();
-                    continue
-                }
-                _ => {}
-            }
-
-            match (op1.as_ref().unwrap(), op2.as_ref().unwrap()) {
-                (OpMethod::Delete(i), _) => {
-                    new_patch.delete(*i);
-                    op1 = ops1.next();
-                }
-                (_, OpMethod::Insert(s)) => {
-                    new_patch.insert(s);
-                    op2 = ops2.next();
-                }
-                (OpMethod::Retain(i), OpMethod::Retain(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        new_patch.retain(*i);
-                        op2 = Some(OpMethod::Retain(*j - i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        new_patch.retain(*j);
-                        op1 = Some(OpMethod::Retain(i - *j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        new_patch.retain(*i);
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-                (OpMethod::Insert(s), OpMethod::Delete(j)) => {
-                    let chars = str_to_chars(s);
-                    let chars_len = chars.len() as u64;
-                    match chars_len.cmp(j) {
-                        Ordering::Less => {
-                            op1 = ops1.next();
-                            op2 = Some(OpMethod::Delete(j - chars_len));
-                        }
-                        Ordering::Greater => {
-                            let st = chars.into_iter().skip(*j as usize).collect();
-                            op1 = Some(OpMethod::Insert(st));
-                            op2 = ops2.next();
-                        }
-                        Ordering::Equal => {
-                            op1 = ops1.next();
-                            op2 = ops2.next();
-                        }
-                    }
-                }
-                (OpMethod::Insert(s), OpMethod::Retain(j)) => {
-                    let chars = str_to_chars(s);
-                    let chars_len = chars.len() as u64;
-                    match chars_len.cmp(j) {
-                        Ordering::Less => {
-                            new_patch.insert(s);
-                            op1 = ops1.next();
-                            op2 = Some(OpMethod::Retain(*j - chars_len));
-                        }
-                        Ordering::Greater => {
-                            let st = chars.into_iter().take(*j as usize).collect::<String>();
-                            new_patch.insert(&st);
-                            op1 = Some(OpMethod::Insert(st));
-                            op2 = ops2.next();
-                        }
-                        Ordering::Equal => {
-                            new_patch.insert(s);
-                            op1 = ops1.next();
-                            op2 = ops2.next();
-                        }
-                    }
-                }
-                (OpMethod::Retain(i), OpMethod::Delete(j)) => match i.cmp(j) {
-                    Ordering::Less => {
-                        new_patch.delete(*i);
-                        op2 = Some(OpMethod::Delete(*j - *i));
-                        op1 = ops1.next();
-                    }
-                    Ordering::Greater => {
-                        new_patch.delete(*j);
-                        op1 = Some(OpMethod::Retain(*i - *j));
-                        op2 = ops2.next();
-                    }
-                    Ordering::Equal => {
-                        new_patch.delete(*j);
-                        op1 = ops1.next();
-                        op2 = ops2.next();
-                    }
-                },
-            };
-        }
-
-        new_patch
-    }
-
-    pub fn colorize(&self) -> String {
-        if self.ops.0.is_empty() {
-            return fg_green(&self.base)
-        }
-
-        let mut st = vec![];
-        st.extend(str_to_chars(&self.base));
-        let st = &mut st.iter();
-
-        let mut colorized_str: Vec<String> = vec![];
-
-        for op in self.ops.0.iter() {
-            match op {
-                OpMethod::Retain(n) => {
-                    for c in st.take(*n as usize) {
-                        colorized_str.push(c.to_string());
-                    }
-                }
-                OpMethod::Delete(n) => {
-                    let mut deleted_part = vec![];
-                    for _ in 0..*n {
-                        let s = st.next();
-                        if let Some(s) = s {
-                            deleted_part.push(s.to_string());
-                        }
-                    }
-                    colorized_str.push(fg_red(&deleted_part.join("")));
-                }
-                OpMethod::Insert(insert) => {
-                    let chars = str_to_chars(insert);
-                    colorized_str.push(fg_green(&chars.join("")))
-                }
-            }
-        }
-
-        colorized_str.join("")
-    }
-}
-
-impl Decodable for OpMethod {
-    fn decode<D: io::Read>(mut d: D) -> core::result::Result<Self, io::Error> {
-        let com: u8 = Decodable::decode(&mut d)?;
-        match com {
-            0 => {
-                let i: u64 = Decodable::decode(&mut d)?;
-                Ok(Self::Delete(i))
-            }
-            1 => {
-                let t: String = Decodable::decode(d)?;
-                Ok(Self::Insert(t))
-            }
-            2 => {
-                let i: u64 = Decodable::decode(&mut d)?;
-                Ok(Self::Retain(i))
-            }
-            _ => Err(io::Error::new(io::ErrorKind::Other, "Parse OpMethod failed")),
-        }
-    }
-}
-
-impl Encodable for OpMethod {
-    fn encode<S: io::Write>(&self, mut s: S) -> core::result::Result<usize, io::Error> {
-        let len: usize = match self {
-            Self::Delete(i) => (0_u8).encode(&mut s)? + i.encode(&mut s)?,
-            Self::Insert(t) => (1_u8).encode(&mut s)? + t.encode(&mut s)?,
-            Self::Retain(i) => (2_u8).encode(&mut s)? + i.encode(&mut s)?,
-        };
-        Ok(len)
-    }
-}
-
-impl Encodable for OpMethods {
-    fn encode<S: io::Write>(&self, mut s: S) -> core::result::Result<usize, io::Error> {
-        let mut len = 0;
-        len += VarInt(self.0.len() as u64).encode(&mut s)?;
-        for c in self.0.iter() {
-            len += c.encode(&mut s)?;
-        }
-        Ok(len)
-    }
-}
-
-impl Decodable for OpMethods {
-    fn decode<D: io::Read>(mut d: D) -> core::result::Result<Self, io::Error> {
-        let len = VarInt::decode(&mut d)?.0;
-        let mut ret = Vec::with_capacity(len as usize);
-        for _ in 0..len {
-            ret.push(Decodable::decode(&mut d)?);
-        }
-        Ok(Self(ret))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use darkfi::raft::gen_id;
-    use darkfi_serial::{deserialize, serialize};
-
-    #[test]
-    fn test_to_string() {
-        let mut patch = Patch::new("", &gen_id(30), "", "");
-        patch.base = "text example\n hello".to_string();
-        patch.retain(14);
-        patch.delete(5);
-        patch.insert("hey");
-
-        assert_eq!(patch.to_string(), "text example\n hey");
-    }
-
-    #[test]
-    fn test_merge() {
-        let mut patch_init = Patch::new("", &gen_id(30), "", "");
-        let base = "text example\n hello";
-        patch_init.base = base.to_string();
-
-        let mut patch1 = patch_init.clone();
-        patch1.retain(14);
-        patch1.delete(5);
-        patch1.insert("hey");
-
-        let mut patch2 = patch_init.clone();
-        patch2.retain(14);
-        patch2.delete(5);
-        patch2.insert("test");
-
-        patch1.merge(&patch2);
-
-        let patch3 = patch1.merge(&patch2);
-
-        assert_eq!(patch3.to_string(), "text example\n test");
-
-        let mut patch1 = patch_init.clone();
-        patch1.retain(5);
-        patch1.delete(7);
-        patch1.insert("ex");
-        patch1.retain(7);
-
-        let mut patch2 = patch_init;
-        patch2.delete(4);
-        patch2.insert("new");
-        patch2.retain(13);
-
-        let patch3 = patch1.merge(&patch2);
-
-        assert_eq!(patch3.to_string(), "new ex\n hello");
-    }
-
-    #[test]
-    fn test_transform() {
-        let mut patch_init = Patch::new("", &gen_id(30), "", "");
-        let base = "text example\n hello";
-        patch_init.base = base.to_string();
-
-        let mut patch1 = patch_init.clone();
-        patch1.retain(14);
-        patch1.delete(5);
-        patch1.insert("hey");
-
-        let mut patch2 = patch_init.clone();
-        patch2.retain(14);
-        patch2.delete(5);
-        patch2.insert("test");
-
-        let patch3 = patch1.transform(&patch2);
-        let patch4 = patch1.merge(&patch3);
-
-        assert_eq!(patch4.to_string(), "text example\n heytest");
-
-        let mut patch1 = patch_init.clone();
-        patch1.retain(5);
-        patch1.delete(7);
-        patch1.insert("ex");
-        patch1.retain(7);
-
-        let mut patch2 = patch_init;
-        patch2.delete(4);
-        patch2.insert("new");
-        patch2.retain(13);
-
-        let patch3 = patch1.transform(&patch2);
-        let patch4 = patch1.merge(&patch3);
-
-        assert_eq!(patch4.to_string(), "new ex\n hello");
-    }
-
-    #[test]
-    fn test_transform2() {
-        let mut patch_init = Patch::new("", &gen_id(30), "", "");
-        let base = "#hello\n hello";
-        patch_init.base = base.to_string();
-
-        let mut patch1 = patch_init.clone();
-        patch1.retain(13);
-        patch1.insert(" world");
-
-        let mut patch2 = patch_init;
-        patch2.retain(1);
-        patch2.delete(5);
-        patch2.insert("this is the title");
-        patch2.retain(7);
-        patch2.insert("\n this is the content");
-
-        let patch3 = patch1.transform(&patch2);
-        let patch4 = patch1.merge(&patch3);
-
-        assert_eq!(patch4.to_string(), "#this is the title\n hello world\n this is the content");
-    }
-
-    #[test]
-    fn test_serialize() {
-        // serialize & deserialize OpMethod
-        let op_method = OpMethod::Delete(3);
-
-        let op_method_ser = serialize(&op_method);
-        let op_method_deser = deserialize(&op_method_ser).unwrap();
-
-        assert_eq!(op_method, op_method_deser);
-
-        // serialize & deserialize Patch
-        let mut patch = Patch::new("", &gen_id(30), "", "");
-        patch.insert("hello");
-        patch.delete(2);
-
-        let patch_ser = serialize(&patch);
-        let patch_deser = deserialize(&patch_ser).unwrap();
-
-        assert_eq!(patch, patch_deser);
-    }
-}

+ 0 - 172
bin/darkwiki/darkwikid/src/util.rs

@@ -1,172 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{
-    collections::HashMap,
-    fs::{create_dir_all, read_dir},
-    path::{Path, PathBuf},
-};
-
-use darkfi_serial::{deserialize, serialize};
-use dryoc::{
-    classic::crypto_secretbox::{crypto_secretbox_easy, crypto_secretbox_open_easy, Key, Nonce},
-    constants::CRYPTO_SECRETBOX_MACBYTES,
-    dryocbox::NewByteArray,
-};
-use log::{error, info, warn};
-use unicode_segmentation::UnicodeSegmentation;
-
-use darkfi::{util::path::expand_path, Error, Result};
-
-use crate::{Args, EncryptedPatch, Patch};
-
-/// Split a `&str` into a vector of each of its chars.
-pub fn str_to_chars(s: &str) -> Vec<&str> {
-    s.graphemes(true).collect::<Vec<&str>>()
-}
-
-/// Parse a base58 string for a `crypto_secretbox` secret.
-fn parse_b58_secret(s: &str) -> Result<[u8; 32]> {
-    match bs58::decode(s).into_vec() {
-        Ok(v) => {
-            if v.len() != 32 {
-                return Err(Error::Custom("Secret is not 32 bytes long".to_string()))
-            }
-
-            Ok(v.try_into().unwrap())
-        }
-        Err(e) => Err(Error::Custom(format!("Unable to parse secret from base58: {}", e))),
-    }
-}
-
-/// Parse a TOML string for configured workspaces and return an `HashMap`
-/// of parsed data. Does not error on failures, just warns if something is
-/// misconfigured.
-pub fn parse_workspaces(toml_str: &str) -> HashMap<String, Key> {
-    let mut ret = HashMap::new();
-
-    let settings: Args = match toml::from_str(toml_str) {
-        Ok(v) => v,
-        Err(e) => {
-            error!("Failed parsing TOML from string: {}", e);
-            return ret
-        }
-    };
-
-    for workspace in settings.workspace {
-        let wrk: Vec<&str> = workspace.split(':').collect();
-        if wrk.len() != 2 {
-            warn!("Invalid workspace: {}", workspace);
-            continue
-        }
-
-        let secret = match parse_b58_secret(wrk[1]) {
-            Ok(v) => v,
-            Err(e) => {
-                warn!("Failed parsing secret for workspace {}: {}", wrk[0], e);
-                continue
-            }
-        };
-
-        let docs_path = match expand_path(&settings.docs) {
-            Ok(v) => v,
-            Err(e) => {
-                warn!("Failed expanding docs path for workspace {}: {}", wrk[0], e);
-                continue
-            }
-        };
-
-        if let Err(e) = create_dir_all(docs_path.join(wrk[0])) {
-            warn!("Failed creating directory for workspace {}: {}", wrk[0], e);
-            continue
-        }
-
-        info!("Added parsed workspace: {}", wrk[0]);
-        ret.insert(wrk[0].to_string(), secret);
-    }
-
-    ret
-}
-
-/// Encrypt a patch using a NaCl crypto_secretbox given a `Patch` and a `Key`.
-pub fn encrypt_patch(patch: &Patch, key: &Key) -> Result<EncryptedPatch> {
-    let nonce = Nonce::gen();
-    let payload = serialize(patch);
-
-    let mut ciphertext = vec![0u8; payload.len() + CRYPTO_SECRETBOX_MACBYTES];
-
-    if let Err(e) = crypto_secretbox_easy(&mut ciphertext, &payload, &nonce, key) {
-        error!("encrypt_patch: Failed encrypting patch: {}", e);
-        return Err(Error::Custom(format!("Failed encrypting darkwiki patch: {}", e)))
-    }
-
-    Ok(EncryptedPatch { nonce, ciphertext })
-}
-
-/// Decrypt a patch using a NaCl crypto_secretbox given an `EncryptedPatch` and a `Key`.
-pub fn decrypt_patch(patch: &EncryptedPatch, key: &Key) -> Result<Patch> {
-    let nonce = &patch.nonce;
-    let ciphertext = &patch.ciphertext;
-
-    let mut decrypted = vec![0u8; ciphertext.len() - CRYPTO_SECRETBOX_MACBYTES];
-    if let Err(e) = crypto_secretbox_open_easy(&mut decrypted, ciphertext, nonce, key) {
-        error!("decrypt_patch: Failed decrypting patch: {}", e);
-        return Err(Error::Custom(format!("Failed decrypting darkwiki patch: {}", e)))
-    }
-
-    Ok(deserialize(&decrypted)?)
-}
-
-/// TODO: DOCUMENT ME
-/// FIXME: There's checking of file extensions here. Take care that the rest of the code
-/// is robust against this attack.
-pub fn get_docs_paths(files: &mut Vec<PathBuf>, path: &Path, parent: Option<&Path>) -> Result<()> {
-    let docs = read_dir(path)?;
-    let docs = docs.filter(|d| d.is_ok()).map(|d| d.unwrap().path()).collect::<Vec<PathBuf>>();
-
-    for doc in docs {
-        if let Some(f) = doc.file_name() {
-            let filename = PathBuf::from(f);
-            let filename = if let Some(parent) = parent { parent.join(filename) } else { filename };
-
-            if doc.is_file() {
-                if let Some(ext) = doc.extension() {
-                    if ext == "md" || ext == "markdown" {
-                        files.push(filename);
-                    }
-                }
-            } else if doc.is_dir() {
-                if f == ".log" {
-                    continue
-                }
-
-                get_docs_paths(files, &doc, Some(&filename))?;
-            }
-        }
-    }
-
-    Ok(())
-}
-
-/// Hash a path and workspace, and encode with base58, providing an ID.
-pub fn path_to_id(path: &str, workspace: &str) -> String {
-    let mut hasher = blake3::Hasher::new();
-    hasher.update(path.as_bytes());
-    hasher.update(workspace.as_bytes());
-    bs58::encode(hasher.finalize().as_bytes()).into_string()
-}

+ 0 - 1
doc/src/SUMMARY.md

@@ -83,7 +83,6 @@
   - [tau](misc/tau.md)
   - [event_graph](misc/event_graph/event_graph.md)
     - [Network Protocol](misc/event_graph/network_protocol.md)
-  - [darkwiki](misc/darkwiki.md)
   - [dnetview](misc/dnetview.md)
 - [Zero2darkfi](zero2darkfi/zero2darkfi.md)
   - [darkmap](zero2darkfi/darkmap.md)

+ 0 - 10
doc/src/clients/portranges.md

@@ -1,10 +0,0 @@
-# P2P port ranges
-
-Standard port ranges used by DarkFi.
-
-* lilith: 25551
-* darkfid-sync: 33032
-* darkfid-consensus: 33033
-* ircd: 25551
-* taud: 23331
-* darkwikid: 24331

+ 0 - 56
doc/src/misc/darkwiki.md

@@ -1,56 +0,0 @@
-# Darkwiki
-
-Collaborative wiki using peer-to-peer network and raft consensus.
-
-## Install
-
-```shell
-% git clone https://github.com/darkrenaissance/darkfi
-% cd darkfi
-% make BINS="darkwiki darkwikid"
-% sudo make install BINS="darkwikid darkwiki"
-```
-
-## Usage
-
-1 - Once Darkwiki get installed, darkwiki daemon must run in the background:
-
-```shell
-% darkwikid
-```
-
-2 - To update `synchronized directory` (default ~/darkwiki) and receive new documents from the network:
-
-```shell
-% darkwiki update
-```
-
-> **_NOTE:_**  The `synchronized directory` path can be changed from the config file in ~/.config/darkfi/darkwiki.toml
-
-3 - After add/edit a document in ~/darkwiki, the changes will be published by running
-  `update` command:
-
-```shell
-% darkwiki update
-```
-
-4 - For restore files having local changes to the original text: 
-
-```shell
-% darkwiki restore
-```
-
-5 - For both `restore` and `update` commands, the flag `--dry-run` can show the changes without applying/publishing the patches
-
-```shell
-% darkwiki update --dry-run
-```
-
-6 - Both `restore` and `update` commands are accepting passing the files names instead of updating/restoring all the documents in ~/darkwiki
-
-```shell
-% darkwiki update file1.md file2.md 
-```
-
-
-