فهرست منبع

script/research: create raft diagnostic tool

ghassmo 4 سال پیش
والد
کامیت
6d9177691b

+ 0 - 1
bin/tau/taud/src/main.rs

@@ -8,7 +8,6 @@ use fxhash::FxHashMap;
 use log::{debug, error, info, warn};
 use smol::future;
 use structopt_toml::StructOptToml;
-use fxhash::FxHashMap;
 
 use darkfi::{
     async_daemonize, net,

+ 0 - 1
script/research/raft-tool/.gitignore → script/research/raft-diag/.gitignore

@@ -1,3 +1,2 @@
 /target
 Cargo.lock
-raft_db

+ 39 - 0
script/research/raft-diag/Cargo.toml

@@ -0,0 +1,39 @@
+[package]
+name = "raft-diag"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+darkfi = {path = "../../../", features = ["raft"]}
+
+# Async
+smol = "1.2.5"
+async-std = {version = "1.12.0", features = ["attributes"]}
+async-trait = "0.1.56"
+async-channel = "1.6.1"
+async-executor = "1.4.1"
+easy-parallel = "3.2.0"
+futures = "0.3.21"
+
+# Misc
+log = "0.4.17"
+simplelog = "0.12.0"
+rand = "0.8.5"
+chrono = "0.4.19"
+thiserror = "1.0.31"
+ctrlc-async = {version= "3.2.2", default-features = false, features = ["async-std", "termination"]}
+url = "2.2.2"
+fxhash = "0.2.1"
+
+# Encoding and parsing
+serde = {version = "1.0.138", features = ["derive"]}
+serde_json = "1.0.82"
+structopt = "0.3.26"
+hex = "0.4.3"
+bs58 = "0.4.0"
+toml = "0.5.9"
+
+[workspace]
+

+ 228 - 0
script/research/raft-diag/src/main.rs

@@ -0,0 +1,228 @@
+use async_std::sync::{Arc, Mutex};
+use std::path::Path;
+
+use async_executor::Executor;
+use fxhash::FxHashMap;
+use log::{error, info, warn};
+use smol::future;
+use structopt::StructOpt;
+use url::Url;
+
+use darkfi::{
+    net,
+    raft::{DataStore, NetMsg, ProtocolRaft, Raft},
+    util::{
+        cli::{get_log_config, get_log_level},
+        expand_path,
+        serial::{SerialDecodable, SerialEncodable},
+        sleep,
+    },
+    Result,
+};
+
+#[derive(Clone, Debug, StructOpt)]
+#[structopt(name = "raft-diag")]
+pub struct Args {
+    /// JSON-RPC listen URL
+    #[structopt(long = "rpc", default_value = "tcp://127.0.0.1:12055")]
+    pub rpc_listen: Url,
+    /// Inbound listen URL
+    #[structopt(long = "inbound")]
+    pub inbound_url: Option<Url>,
+    /// Seed Urls
+    #[structopt(long = "seeds")]
+    pub seed_urls: Vec<Url>,
+    /// Outbound connections
+    #[structopt(long = "outbound", default_value = "0")]
+    pub outbound_connections: u32,
+    /// Sets Datastore Path
+    #[structopt(long = "path", default_value = "test1.db")]
+    pub datastore: String,
+    /// Check if all datastore paths provided are synced
+    #[structopt(long = "check")]
+    pub check: Vec<String>,
+    /// Datastore path to extract and print it
+    #[structopt(long = "extract")]
+    pub extract: Option<String>,
+    /// Number of messages to broadcast
+    #[structopt(short, default_value = "0")]
+    pub broadcast: u32,
+    /// Increase verbosity
+    #[structopt(short, parse(from_occurrences))]
+    pub verbose: u8,
+}
+
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable, PartialEq, Eq)]
+pub struct Message {
+    payload: String,
+}
+
+fn extract(path: &str) -> Result<()> {
+    if !Path::new(path).exists() {
+        return Ok(())
+    }
+
+    let db = DataStore::<Message>::new(path)?;
+    let commits = db.commits.get_all()?;
+
+    println!("{:?}", commits);
+
+    Ok(())
+}
+
+fn check(args: Args) -> Result<()> {
+    let mut commits_check = vec![];
+
+    for path in args.check {
+        if !Path::new(&path).exists() {
+            continue
+        }
+        let db = DataStore::<Message>::new(&path)?;
+        let commits = db.commits.get_all()?;
+        commits_check.push(commits);
+    }
+
+    let result = commits_check.windows(2).all(|w| w[0] == w[1]);
+
+    println!("Synced: {}", result);
+
+    Ok(())
+}
+
+async fn start_broadcasting(n: u32, sender: async_channel::Sender<Message>) -> Result<()> {
+    sleep(10).await;
+    info!("Start broadcasting...");
+    for id in 0..n {
+        sleep(3).await;
+        let msg = format!("msg_test_{}", id);
+        info!("Send a message {:?}", msg);
+        let msg = Message { payload: msg };
+        sender.send(msg).await?;
+    }
+
+    Ok(())
+}
+
+async fn receive_loop(receiver: async_channel::Receiver<Message>) -> Result<()> {
+    loop {
+        let msg = receiver.recv().await?;
+        info!("Receive new msg {:?}", msg);
+    }
+}
+
+async fn start(args: Args, executor: Arc<Executor<'_>>) -> Result<()> {
+    let net_settings = net::Settings {
+        outbound_connections: args.outbound_connections,
+        inbound: args.inbound_url.clone(),
+        external_addr: args.inbound_url,
+        seeds: args.seed_urls,
+        ..net::Settings::default()
+    };
+
+    //
+    // Raft
+    //
+
+    let datastore_raft = expand_path(&args.datastore)?;
+
+    let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
+
+    let mut raft =
+        Raft::<Message>::new(net_settings.inbound.clone(), datastore_raft, seen_net_msgs.clone())?;
+
+    //
+    // P2p setup
+    //
+
+    let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
+
+    let p2p = net::P2p::new(net_settings).await;
+    let p2p = p2p.clone();
+
+    let registry = p2p.protocol_registry();
+
+    let raft_node_id = raft.id.clone();
+    registry
+        .register(net::SESSION_ALL, move |channel, p2p| {
+            let raft_node_id = raft_node_id.clone();
+            let sender = p2p_send_channel.clone();
+            let seen_net_msgs_cloned = seen_net_msgs.clone();
+            async move {
+                ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
+            }
+        })
+        .await;
+
+    p2p.clone().start(executor.clone()).await?;
+
+    executor.spawn(p2p.clone().run(executor.clone())).detach();
+
+    //
+    // Waiting Exit signal
+    //
+    let (signal, shutdown) = async_channel::bounded::<()>(1);
+    ctrlc_async::set_async_handler(async move {
+        warn!("Catch exit signal");
+        // cleaning up tasks running in the background
+        if let Err(e) = signal.send(()).await {
+            error!("Error on sending exit signal: {}", e);
+        }
+    })
+    .unwrap();
+
+    if args.broadcast != 0 {
+        executor.spawn(start_broadcasting(args.broadcast, raft.get_msgs_channel())).detach();
+    }
+
+    executor.spawn(receive_loop(raft.get_commits_channel())).detach();
+
+    raft.start(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let args = Args::from_args();
+    let log_level = get_log_level(args.verbose.into());
+    let log_config = get_log_config();
+
+    let mut log_path = expand_path(&args.datastore)?;
+    let log_name: String = log_path.file_name().as_ref().unwrap().to_str().unwrap().to_owned();
+    log_path.pop();
+    let log_path = log_path.join(&format!("{}.log", log_name));
+    let env_log_file_path = std::fs::File::create(log_path).unwrap();
+
+    simplelog::CombinedLogger::init(vec![
+        simplelog::TermLogger::new(
+            log_level,
+            log_config.clone(),
+            simplelog::TerminalMode::Mixed,
+            simplelog::ColorChoice::Auto,
+        ),
+        simplelog::WriteLogger::new(log_level, log_config, env_log_file_path),
+    ])?;
+
+    if !args.check.is_empty() {
+        return check(args)
+    }
+
+    if args.extract.is_some() {
+        return extract(&args.extract.unwrap())
+    }
+
+    // https://docs.rs/smol/latest/smol/struct.Executor.html#examples
+    let ex = Arc::new(async_executor::Executor::new());
+    let (signal, shutdown) = async_channel::unbounded::<()>();
+    let (_, result) = easy_parallel::Parallel::new()
+        // Run four executor threads
+        .each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
+        // Run the main future on the current thread.
+        .finish(|| {
+            future::block_on(async {
+                start(args, ex.clone()).await?;
+                drop(signal);
+                Ok::<(), darkfi::Error>(())
+            })
+        });
+
+    result
+}

+ 12 - 0
script/research/raft-diag/tmux_sessions.sh

@@ -0,0 +1,12 @@
+#!/bin/sh
+export LOG_TARGETS='!net,!sled,!rustls' 
+
+tmux new-session -d "./target/release/raft-diag --inbound tcp://127.0.0.1:12001 --path test1.db -v"
+sleep 3
+tmux split-window -v "./target/release/raft-diag --inbound tcp://127.0.0.1:12002 --seeds tcp://127.0.0.1:12001 --outbound 3 --path test2.db -v"
+sleep 2
+tmux split-window -h "./target/release/raft-diag  --seeds tcp://127.0.0.1:12001 --outbound 3 --path test3.db -v"
+sleep 1
+tmux select-pane -t 0
+tmux split-window -h "./target/release/raft-diag  --seeds tcp://127.0.0.1:12001 --outbound 3 --path test4.db -b 3 -v"
+tmux attach

+ 0 - 28
script/research/raft-tool/Cargo.toml

@@ -1,28 +0,0 @@
-[package]
-name = "raft-tool"
-version = "0.3.0"
-edition = "2021"
-
-[dependencies.darkfi]
-path = "../../../"
-features = ["raft"]
-
-[dependencies]
-
-async-std = "1.12.0"
-sled = "0.34.7"
-
-# for taud
-crypto_box = {version = "0.7.2", features = ["std"]}                             
-hex = "0.4.3"
-log = "0.4.17"                                                                   
-simplelog = "0.12.0"                                                             
-rand = "0.8.5"                                                                   
-chrono = "0.4.19"                                                                
-url = "2.2.2"                                                                    
-serde = {version = "1.0.138", features = ["derive"]}                             
-serde_json = "1.0.82" 
-thiserror = "1.0.31"                                                             
-
-
-[workspace]

+ 0 - 45
script/research/raft-tool/src/error.rs

@@ -1,45 +0,0 @@
-use serde_json::Value;
-
-use darkfi::rpc::jsonrpc::{ErrorCode, JsonError, JsonResponse, JsonResult};
-
-#[derive(Debug, thiserror::Error)]
-pub enum TaudError {
-    #[error("Due timestamp invalid")]
-    InvalidDueTime,
-    #[error("Invalid Id")]
-    InvalidId,
-    #[error("Invalid Data/Params: `{0}` ")]
-    InvalidData(String),
-    #[error("InternalError")]
-    Darkfi(#[from] darkfi::error::Error),
-    #[error("Json serialization error: `{0}`")]
-    SerdeJsonError(String),
-}
-
-pub type TaudResult<T> = std::result::Result<T, TaudError>;
-
-impl From<serde_json::Error> for TaudError {
-    fn from(err: serde_json::Error) -> TaudError {
-        TaudError::SerdeJsonError(err.to_string())
-    }
-}
-
-pub fn to_json_result(res: TaudResult<Value>, id: Value) -> JsonResult {
-    match res {
-        Ok(v) => JsonResponse::new(v, id).into(),
-        Err(err) => match err {
-            TaudError::InvalidId => {
-                JsonError::new(ErrorCode::InvalidParams, Some("invalid task id".into()), id).into()
-            }
-            TaudError::InvalidData(e) | TaudError::SerdeJsonError(e) => {
-                JsonError::new(ErrorCode::InvalidParams, Some(e), id).into()
-            }
-            TaudError::InvalidDueTime => {
-                JsonError::new(ErrorCode::InvalidParams, Some("invalid due time".into()), id).into()
-            }
-            TaudError::Darkfi(e) => {
-                JsonError::new(ErrorCode::InternalError, Some(e.to_string()), id).into()
-            }
-        },
-    }
-}

+ 0 - 203
script/research/raft-tool/src/main.rs

@@ -1,203 +0,0 @@
-use std::{fs::File, io::Write};
-
-use crypto_box::{aead::Aead, Box, SecretKey, KEY_SIZE};
-
-use darkfi::{
-    raft::DataStore,
-    util::{
-        expand_path,
-        serial::{deserialize, SerialDecodable, SerialEncodable},
-    },
-    Result,
-};
-
-mod error;
-mod month_tasks;
-mod task_info;
-mod util;
-
-use crate::{task_info::TaskInfo, util::load};
-
-#[derive(Debug)]
-pub struct Info<T> {
-    pub logs: Vec<Log<T>>,
-    pub commits: Vec<T>,
-    pub voted_for: Vec<Option<NodeId>>,
-    pub terms: Vec<u64>,
-}
-
-#[derive(Debug)]
-pub struct Log<T> {
-    pub term: u64,
-    pub msg: T,
-}
-
-#[derive(Debug)]
-pub struct NodeId(Vec<u8>);
-
-#[derive(Debug, SerialEncodable, SerialDecodable)]
-struct EncryptedTask {
-    nonce: Vec<u8>,
-    payload: Vec<u8>,
-}
-
-fn decrypt_task(encrypt_task: &EncryptedTask, secret_key: &SecretKey) -> Option<TaskInfo> {
-    let public_key = secret_key.public_key();
-    let msg_box = Box::new(&public_key, secret_key);
-
-    let nonce = encrypt_task.nonce.as_slice();
-    let decrypted_task = match msg_box.decrypt(nonce.into(), &encrypt_task.payload[..]) {
-        Ok(m) => m,
-        Err(_) => return None,
-    };
-
-    deserialize(&decrypted_task).ok()
-}
-
-type PrivmsgId = u32;
-
-#[derive(Debug, SerialEncodable, SerialDecodable)]
-struct Privmsg {
-    id: PrivmsgId,
-    nickname: String,
-    channel: String,
-    message: String,
-}
-
-fn extract_taud() -> Result<String> {
-    let db_path = expand_path(&"~/.config/darkfi/tau/tau.db").unwrap();
-    let datastore = DataStore::<EncryptedTask>::new(&db_path.to_str().unwrap())?;
-
-    let sk_path = expand_path(&"~/.config/darkfi/tau/secret_key").unwrap();
-
-    let sk = {
-        let loaded_key = load::<String>(&sk_path);
-
-        if loaded_key.is_err() {
-            log::error!(
-                "Could not load secret key from file, \
-                  please run \"taud --help\" for more information"
-            );
-            return Ok("Load secret_key error".into())
-        }
-
-        let sk_bytes = hex::decode(loaded_key.unwrap())?;
-        let sk_bytes: [u8; KEY_SIZE] = sk_bytes.as_slice().try_into()?;
-        SecretKey::try_from(sk_bytes)?
-    };
-
-    println!("Extracting db from: {:?}", db_path);
-
-    // Retrieve all data trees
-    let sled_logs = datastore.logs.get_all()?;
-    let sled_commits = datastore.commits.get_all()?;
-    let sled_voted_for = datastore.voted_for.get_all()?;
-    let sled_terms = datastore.current_term.get_all()?;
-
-    // Parse retrieved data trees
-    println!("Data extracted, parsing to viewable form...");
-
-    // Logs
-    let mut logs = vec![];
-    for log in sled_logs {
-        let encrypt_task: EncryptedTask = deserialize(&log.msg)?;
-        let task_info = decrypt_task(&encrypt_task, &sk).unwrap();
-        logs.push(Log { term: log.term, msg: task_info });
-    }
-
-    // Commits
-    let mut commits = vec![];
-    for commit in &sled_commits {
-        let task_info = decrypt_task(
-            &EncryptedTask { nonce: commit.nonce.clone(), payload: commit.payload.clone() },
-            &sk,
-        )
-        .unwrap();
-        commits.push(task_info);
-    }
-
-    // Voted for
-    let mut voted_for = vec![];
-    for vote in sled_voted_for {
-        match vote {
-            Some(v) => voted_for.push(Some(NodeId(v.0.clone()))),
-            None => voted_for.push(None),
-        }
-    }
-
-    // Terms
-    let mut terms = vec![];
-    for term in sled_terms {
-        terms.push(term);
-    }
-
-    let info = Info::<TaskInfo> { logs, commits, voted_for, terms };
-    let info_string = format!("{:#?}", info);
-    Ok(info_string)
-}
-
-fn extract_ircd() -> Result<String> {
-    let db_path = expand_path(&"~/.config/darkfi/ircd/ircd.db").unwrap();
-    let datastore = DataStore::<Privmsg>::new(&db_path.to_str().unwrap())?;
-    println!("Extracting db from: {:?}", db_path);
-
-    // Retrieve all data trees
-    let sled_logs = datastore.logs.get_all()?;
-    let sled_commits = datastore.commits.get_all()?;
-    let sled_voted_for = datastore.voted_for.get_all()?;
-    let sled_terms = datastore.current_term.get_all()?;
-
-    // Parse retrieved data trees
-    println!("Data extracted, parsing to viewable form...");
-
-    // Logs
-    let mut logs = vec![];
-    for log in sled_logs {
-        logs.push(Log { term: log.term, msg: deserialize(&log.msg)? });
-    }
-
-    // Commits
-    let mut commits = vec![];
-    for commit in &sled_commits {
-        commits.push(Privmsg {
-            id: commit.id,
-            nickname: commit.nickname.clone(),
-            channel: commit.channel.clone(),
-            message: commit.message.clone(),
-        });
-    }
-
-    // Voted for
-    let mut voted_for = vec![];
-    for vote in sled_voted_for {
-        match vote {
-            Some(v) => voted_for.push(Some(NodeId(v.0.clone()))),
-            None => voted_for.push(None),
-        }
-    }
-
-    // Terms
-    let mut terms = vec![];
-    for term in sled_terms {
-        terms.push(term);
-    }
-
-    let info = Info::<Privmsg> { logs, commits, voted_for, terms };
-    let info_string = format!("{:#?}", info);
-
-    Ok(info_string)
-}
-
-#[async_std::main]
-async fn main() -> Result<()> {
-    let info_string = extract_taud()?;
-
-    // Generating file
-    let file_path = "raft_db";
-    println!("Data parsed, writing to file {:?}", file_path);
-    let mut file = File::create(file_path)?;
-    file.write(info_string.as_bytes())?;
-    println!("File created!");
-
-    Ok(())
-}

+ 0 - 213
script/research/raft-tool/src/month_tasks.rs

@@ -1,213 +0,0 @@
-use std::{
-    fs, io,
-    path::{Path, PathBuf},
-};
-
-use chrono::{TimeZone, Utc};
-use log::debug;
-use serde::{Deserialize, Serialize};
-
-use darkfi::util::Timestamp;
-
-use crate::{
-    error::{TaudError, TaudResult},
-    task_info::TaskInfo,
-    util::{load, save},
-};
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct MonthTasks {
-    created_at: Timestamp,
-    task_tks: Vec<String>,
-}
-
-impl MonthTasks {
-    pub fn new(task_tks: &[String]) -> Self {
-        Self { created_at: Timestamp::current_time(), task_tks: task_tks.to_owned() }
-    }
-
-    pub fn add(&mut self, ref_id: &str) {
-        debug!(target: "tau", "MonthTasks::add()");
-        if !self.task_tks.contains(&ref_id.into()) {
-            self.task_tks.push(ref_id.into());
-        }
-    }
-
-    pub fn objects(&self, dataset_path: &Path) -> TaudResult<Vec<TaskInfo>> {
-        debug!(target: "tau", "MonthTasks::objects()");
-        let mut tks: Vec<TaskInfo> = vec![];
-
-        for ref_id in self.task_tks.iter() {
-            tks.push(TaskInfo::load(ref_id, dataset_path)?);
-        }
-
-        Ok(tks)
-    }
-
-    pub fn remove(&mut self, ref_id: &str) {
-        debug!(target: "tau", "MonthTasks::remove()");
-        if let Some(index) = self.task_tks.iter().position(|t| *t == ref_id) {
-            self.task_tks.remove(index);
-        }
-    }
-
-    pub fn set_date(&mut self, date: &Timestamp) {
-        debug!(target: "tau", "MonthTasks::set_date()");
-        self.created_at = *date;
-    }
-
-    fn get_path(date: &Timestamp, dataset_path: &Path) -> PathBuf {
-        debug!(target: "tau", "MonthTasks::get_path()");
-        dataset_path.join("month").join(Utc.timestamp(date.0, 0).format("%m%y").to_string())
-    }
-
-    pub fn save(&self, dataset_path: &Path) -> TaudResult<()> {
-        debug!(target: "tau", "MonthTasks::save()");
-        save::<Self>(&Self::get_path(&self.created_at, dataset_path), self)
-            .map_err(TaudError::Darkfi)
-    }
-
-    fn get_all(dataset_path: &Path) -> io::Result<Vec<PathBuf>> {
-        debug!(target: "tau", "MonthTasks::get_all()");
-
-        let mut entries = fs::read_dir(dataset_path.join("month"))?
-            .map(|res| res.map(|e| e.path()))
-            .collect::<Result<Vec<_>, io::Error>>()?;
-
-        entries.sort();
-
-        Ok(entries)
-    }
-
-    fn create(date: &Timestamp, dataset_path: &Path) -> TaudResult<Self> {
-        debug!(target: "tau", "MonthTasks::create()");
-
-        let mut mt = Self::new(&[]);
-        mt.set_date(date);
-        mt.save(dataset_path)?;
-        Ok(mt)
-    }
-
-    pub fn load_or_create(date: Option<&Timestamp>, dataset_path: &Path) -> TaudResult<Self> {
-        debug!(target: "tau", "MonthTasks::load_or_create()");
-
-        // if a date is given we load that date's month tasks
-        // if not, we load tasks from all months
-        match date {
-            Some(date) => match load::<Self>(&Self::get_path(date, dataset_path)) {
-                Ok(mt) => Ok(mt),
-                Err(_) => Self::create(date, dataset_path),
-            },
-            None => {
-                let path_all = match Self::get_all(dataset_path) {
-                    Ok(t) => t,
-                    Err(_) => vec![],
-                };
-
-                let mut loaded_mt = Self::new(&[]);
-
-                for path in path_all {
-                    let mt = load::<Self>(&path)?;
-                    loaded_mt.created_at = mt.created_at;
-                    for tks in mt.task_tks {
-                        if !loaded_mt.task_tks.contains(&tks) {
-                            loaded_mt.task_tks.push(tks)
-                        }
-                    }
-                }
-                Ok(loaded_mt)
-            }
-        }
-    }
-
-    pub fn load_current_open_tasks(dataset_path: &Path) -> TaudResult<Vec<TaskInfo>> {
-        let mt = Self::load_or_create(None, dataset_path)?;
-        Ok(mt.objects(dataset_path)?.into_iter().filter(|t| t.get_state() != "stop").collect())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::{
-        fs::{create_dir_all, remove_dir_all},
-        path::PathBuf,
-    };
-
-    use super::*;
-    use darkfi::Result;
-
-    const TEST_DATA_PATH: &str = "/tmp/test_tau_data";
-
-    fn get_path() -> Result<PathBuf> {
-        remove_dir_all(TEST_DATA_PATH).ok();
-
-        let path = PathBuf::from(TEST_DATA_PATH);
-
-        // mkdir dataset_path if not exists
-        create_dir_all(path.join("month"))?;
-        create_dir_all(path.join("task"))?;
-        Ok(path)
-    }
-
-    #[test]
-    fn load_and_save_tasks() -> TaudResult<()> {
-        let dataset_path = get_path()?;
-
-        // load and save TaskInfo
-        ///////////////////////
-
-        let mut task =
-            TaskInfo::new("test_title", "test_desc", "NICKNAME", None, 0.0, &dataset_path)?;
-
-        task.save(&dataset_path)?;
-
-        let t_load = TaskInfo::load(&task.ref_id, &dataset_path)?;
-
-        assert_eq!(task, t_load);
-
-        task.set_title("test_title_2");
-
-        task.save(&dataset_path)?;
-
-        let t_load = TaskInfo::load(&task.ref_id, &dataset_path)?;
-
-        assert_eq!(task, t_load);
-
-        // load and save MonthTasks
-        ///////////////////////
-
-        let task_tks = vec![];
-
-        let mut mt = MonthTasks::new(&task_tks);
-
-        mt.save(&dataset_path)?;
-
-        let mt_load = MonthTasks::load_or_create(Some(&Timestamp::current_time()), &dataset_path)?;
-
-        assert_eq!(mt, mt_load);
-
-        mt.add(&task.ref_id);
-
-        mt.save(&dataset_path)?;
-
-        let mt_load = MonthTasks::load_or_create(Some(&Timestamp::current_time()), &dataset_path)?;
-
-        assert_eq!(mt, mt_load);
-
-        // activate task
-        ///////////////////////
-
-        let task =
-            TaskInfo::new("test_title_3", "test_desc", "NICKNAME", None, 0.0, &dataset_path)?;
-
-        task.save(&dataset_path)?;
-
-        let mt_load = MonthTasks::load_or_create(Some(&Timestamp::current_time()), &dataset_path)?;
-
-        assert!(mt_load.task_tks.contains(&task.ref_id));
-
-        remove_dir_all(TEST_DATA_PATH).ok();
-
-        Ok(())
-    }
-}

+ 0 - 274
script/research/raft-tool/src/task_info.rs

@@ -1,274 +0,0 @@
-use std::{
-    io,
-    path::{Path, PathBuf},
-};
-
-use log::debug;
-use serde::{Deserialize, Serialize};
-
-use darkfi::util::{
-    serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt},
-    Timestamp,
-};
-
-use crate::{
-    error::{TaudError, TaudResult},
-    month_tasks::MonthTasks,
-    util::{find_free_id, load, random_ref_id, save},
-};
-
-#[derive(Clone, Debug, Serialize, Deserialize, SerialEncodable, SerialDecodable, PartialEq)]
-struct TaskEvent {
-    action: String,
-    timestamp: Timestamp,
-}
-
-impl TaskEvent {
-    fn new(action: String) -> Self {
-        Self { action, timestamp: Timestamp::current_time() }
-    }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, SerialDecodable, SerialEncodable, PartialEq)]
-pub struct Comment {
-    content: String,
-    author: String,
-    timestamp: Timestamp,
-}
-
-impl Comment {
-    pub fn new(content: &str, author: &str) -> Self {
-        Self {
-            content: content.into(),
-            author: author.into(),
-            timestamp: Timestamp::current_time(),
-        }
-    }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct TaskEvents(Vec<TaskEvent>);
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct TaskComments(Vec<Comment>);
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct TaskProjects(Vec<String>);
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct TaskAssigns(Vec<String>);
-
-#[derive(Clone, Debug, Serialize, Deserialize, SerialEncodable, SerialDecodable, PartialEq)]
-pub struct TaskInfo {
-    pub(crate) ref_id: String,
-    id: u32,
-    title: String,
-    desc: String,
-    owner: String,
-    assign: TaskAssigns,
-    project: TaskProjects,
-    due: Option<Timestamp>,
-    rank: f32,
-    created_at: Timestamp,
-    events: TaskEvents,
-    comments: TaskComments,
-}
-
-impl TaskInfo {
-    pub fn new(
-        title: &str,
-        desc: &str,
-        owner: &str,
-        due: Option<Timestamp>,
-        rank: f32,
-        dataset_path: &Path,
-    ) -> TaudResult<Self> {
-        // generate ref_id
-        let ref_id = random_ref_id();
-
-        let created_at = Timestamp::current_time();
-
-        let task_ids: Vec<u32> =
-            MonthTasks::load_current_open_tasks(dataset_path)?.into_iter().map(|t| t.id).collect();
-
-        let id: u32 = find_free_id(&task_ids);
-
-        if let Some(d) = &due {
-            if *d < Timestamp::current_time() {
-                return Err(TaudError::InvalidDueTime)
-            }
-        }
-
-        Ok(Self {
-            ref_id,
-            id,
-            title: title.into(),
-            desc: desc.into(),
-            owner: owner.into(),
-            assign: TaskAssigns(vec![]),
-            project: TaskProjects(vec![]),
-            due,
-            rank,
-            created_at,
-            comments: TaskComments(vec![]),
-            events: TaskEvents(vec![]),
-        })
-    }
-
-    pub fn load(ref_id: &str, dataset_path: &Path) -> TaudResult<Self> {
-        debug!(target: "tau", "TaskInfo::load()");
-        let task = load::<Self>(&Self::get_path(ref_id, dataset_path))?;
-        Ok(task)
-    }
-
-    pub fn save(&self, dataset_path: &Path) -> TaudResult<()> {
-        debug!(target: "tau", "TaskInfo::save()");
-        save::<Self>(&Self::get_path(&self.ref_id, dataset_path), self)
-            .map_err(TaudError::Darkfi)?;
-
-        if self.get_state() == "stop" {
-            self.deactivate(dataset_path)?;
-        } else {
-            self.activate(dataset_path)?;
-        }
-
-        Ok(())
-    }
-
-    pub fn activate(&self, path: &Path) -> TaudResult<()> {
-        debug!(target: "tau", "TaskInfo::activate()");
-        let mut mt = MonthTasks::load_or_create(Some(&self.created_at), path)?;
-        mt.add(&self.ref_id);
-        mt.save(path)
-    }
-
-    pub fn deactivate(&self, path: &Path) -> TaudResult<()> {
-        debug!(target: "tau", "TaskInfo::deactivate()");
-        let mut mt = MonthTasks::load_or_create(Some(&self.created_at), path)?;
-        mt.remove(&self.ref_id);
-        mt.save(path)
-    }
-
-    pub fn get_state(&self) -> String {
-        debug!(target: "tau", "TaskInfo::get_state()");
-        if let Some(ev) = self.events.0.last() {
-            ev.action.clone()
-        } else {
-            "open".into()
-        }
-    }
-
-    fn get_path(ref_id: &str, dataset_path: &Path) -> PathBuf {
-        debug!(target: "tau", "TaskInfo::get_path()");
-        dataset_path.join("task").join(ref_id)
-    }
-
-    pub fn get_id(&self) -> u32 {
-        debug!(target: "tau", "TaskInfo::get_id()");
-        self.id
-    }
-
-    pub fn set_title(&mut self, title: &str) {
-        debug!(target: "tau", "TaskInfo::set_title()");
-        self.title = title.into();
-    }
-
-    pub fn set_desc(&mut self, desc: &str) {
-        debug!(target: "tau", "TaskInfo::set_desc()");
-        self.desc = desc.into();
-    }
-
-    pub fn set_assign(&mut self, assign: &[String]) {
-        debug!(target: "tau", "TaskInfo::set_assign()");
-        self.assign = TaskAssigns(assign.to_owned());
-    }
-
-    pub fn set_project(&mut self, project: &[String]) {
-        debug!(target: "tau", "TaskInfo::set_project()");
-        self.project = TaskProjects(project.to_owned());
-    }
-
-    pub fn set_comment(&mut self, c: Comment) {
-        debug!(target: "tau", "TaskInfo::set_comment()");
-        self.comments.0.push(c);
-    }
-
-    pub fn set_rank(&mut self, r: f32) {
-        debug!(target: "tau", "TaskInfo::set_rank()");
-        self.rank = r;
-    }
-
-    pub fn set_due(&mut self, d: Option<Timestamp>) {
-        debug!(target: "tau", "TaskInfo::set_due()");
-        self.due = d;
-    }
-
-    pub fn set_state(&mut self, action: &str) {
-        debug!(target: "tau", "TaskInfo::set_state()");
-        if self.get_state() == action {
-            return
-        }
-        self.events.0.push(TaskEvent::new(action.into()));
-    }
-}
-
-impl Encodable for TaskEvents {
-    fn encode<S: io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        encode_vec(&self.0, s)
-    }
-}
-
-impl Decodable for TaskEvents {
-    fn decode<D: io::Read>(d: D) -> darkfi::Result<Self> {
-        Ok(Self(decode_vec(d)?))
-    }
-}
-impl Encodable for TaskComments {
-    fn encode<S: io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        encode_vec(&self.0, s)
-    }
-}
-
-impl Decodable for TaskComments {
-    fn decode<D: io::Read>(d: D) -> darkfi::Result<Self> {
-        Ok(Self(decode_vec(d)?))
-    }
-}
-impl Encodable for TaskProjects {
-    fn encode<S: io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        encode_vec(&self.0, s)
-    }
-}
-
-impl Decodable for TaskProjects {
-    fn decode<D: io::Read>(d: D) -> darkfi::Result<Self> {
-        Ok(Self(decode_vec(d)?))
-    }
-}
-
-impl Encodable for TaskAssigns {
-    fn encode<S: io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        encode_vec(&self.0, s)
-    }
-}
-
-impl Decodable for TaskAssigns {
-    fn decode<D: io::Read>(d: D) -> darkfi::Result<Self> {
-        Ok(Self(decode_vec(d)?))
-    }
-}
-
-fn encode_vec<T: Encodable, S: io::Write>(vec: &[T], mut s: S) -> darkfi::Result<usize> {
-    let mut len = 0;
-    len += VarInt(vec.len() as u64).encode(&mut s)?;
-    for c in vec.iter() {
-        len += c.encode(&mut s)?;
-    }
-    Ok(len)
-}
-
-fn decode_vec<T: Decodable, D: io::Read>(mut d: D) -> darkfi::Result<Vec<T>> {
-    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(ret)
-}

+ 0 - 59
script/research/raft-tool/src/util.rs

@@ -1,59 +0,0 @@
-use std::{fs::File, io::BufReader, path::Path};
-
-use rand::{distributions::Alphanumeric, thread_rng, Rng};
-use serde::{de::DeserializeOwned, Serialize};
-
-use darkfi::Result;
-
-pub fn random_ref_id() -> String {
-    thread_rng().sample_iter(&Alphanumeric).take(30).map(char::from).collect()
-}
-
-pub fn find_free_id(task_ids: &[u32]) -> u32 {
-    for i in 1.. {
-        if !task_ids.contains(&i) {
-            return i
-        }
-    }
-    1
-}
-
-pub fn load<T: DeserializeOwned>(path: &Path) -> Result<T> {
-    let file = File::open(path)?;
-    let reader = BufReader::new(file);
-
-    let value: T = serde_json::from_reader(reader)?;
-    Ok(value)
-}
-
-pub fn save<T: Serialize>(path: &Path, value: &T) -> Result<()> {
-    let file = File::create(path)?;
-    serde_json::to_writer_pretty(file, value)?;
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn find_free_id_test() -> Result<()> {
-        let mut ids: Vec<u32> = vec![1, 3, 8, 9, 10, 3];
-        let ids_empty: Vec<u32> = vec![];
-        let ids_duplicate: Vec<u32> = vec![1; 100];
-
-        let find_id = find_free_id(&ids);
-
-        assert_eq!(find_id, 2);
-
-        ids.push(find_id);
-
-        assert_eq!(find_free_id(&ids), 4);
-
-        assert_eq!(find_free_id(&ids_empty), 1);
-
-        assert_eq!(find_free_id(&ids_duplicate), 2);
-
-        Ok(())
-    }
-}