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, RaftSettings}, 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: Vec, /// Seed Urls #[structopt(long = "seeds")] pub seed_urls: Vec, /// 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, /// Datastore path to extract and print it #[structopt(long = "extract")] pub extract: Option, /// 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::::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::::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) -> Result<()> { sleep(8).await; info!(target: "raft", "Start broadcasting..."); for id in 0..n { let msg = format!("msg_test_{}", id); info!(target: "raft", "Send a message {:?}", msg); let msg = Message { payload: msg }; sender.send(msg).await?; } Ok(()) } async fn receive_loop(receiver: async_channel::Receiver) -> Result<()> { loop { let msg = receiver.recv().await?; info!(target: "raft", "Receive new msg {:?}", msg); } } async fn start(args: Args, executor: Arc>) -> 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 raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() }; let mut raft = Raft::::new(raft_settings, seen_net_msgs.clone())?; // // P2p setup // let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::(); let p2p = net::P2p::new(net_settings).await; let p2p = p2p.clone(); let registry = p2p.protocol_registry(); let raft_node_id = raft.id(); 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::set_handler(move || { warn!("Catch exit signal"); // cleaning up tasks running in the background if let Err(e) = async_std::task::block_on(signal.send(())) { error!("Error on sending exit signal: {}", e); } }) .unwrap(); if args.broadcast != 0 { executor.spawn(start_broadcasting(args.broadcast, raft.sender())).detach(); } executor.spawn(receive_loop(raft.receiver())).detach(); raft.run(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 }