diff --git a/nomos-services/http/src/backends/axum.rs b/nomos-services/http/src/backends/axum.rs index 4b103c67..0d473f65 100644 --- a/nomos-services/http/src/backends/axum.rs +++ b/nomos-services/http/src/backends/axum.rs @@ -113,7 +113,7 @@ impl HttpBackend for AxumBackend { let router = self.router.clone(); let service = tower::service_fn(move |request: Request| { let mut router = router.lock().clone(); - async move { router.call(request).await } + router.call(request) }); axum::Server::bind(&self.config.address) @@ -125,8 +125,8 @@ impl HttpBackend for AxumBackend { impl AxumBackend { fn add_data_route(&self, method: HttpMethod, path: &str, req_stream: Sender) { - let handle_data = |Query(query): Query>, payload: Option| async move { - handle_req(req_stream, query, payload).await + let handle_data = |Query(query): Query>, payload: Option| { + handle_req(req_stream, query, payload) }; let handler = match method { diff --git a/simulations/Cargo.toml b/simulations/Cargo.toml index 7b67530a..ba058de7 100644 --- a/simulations/Cargo.toml +++ b/simulations/Cargo.toml @@ -7,9 +7,11 @@ edition = "2021" [dependencies] rand = { version = "0.8", features = ["small_rng"] } -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" +clap = { version = "4", features = ["derive"] } + [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/simulations/src/bin/app.rs b/simulations/src/bin/app.rs index 8df0541b..61569104 100644 --- a/simulations/src/bin/app.rs +++ b/simulations/src/bin/app.rs @@ -1,3 +1,146 @@ -pub fn main() { - println!("WIP"); +use std::{path::PathBuf, str::FromStr}; + +use clap::Parser; +use rand::thread_rng; +use serde::{Deserialize, Serialize}; +use simulations::{ + config::Config, + node::{ + carnot::{CarnotNode, CarnotStep}, + Node, StepTime, + }, + overlay::{flat::FlatOverlay, Overlay}, + runner::{ConsensusRunner, LayoutNodes}, +}; + +/// Simple program to greet a person +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Path for a yaml-encoded network config file + config: std::path::PathBuf, + #[arg(long, default_value_t = OverlayType::Flat)] + overlay_type: OverlayType, + #[arg(long, default_value_t = NodeType::Carnot)] + node_type: NodeType, + #[arg(short, long, default_value_t = OutputType::StdOut)] + output: OutputType, +} + +#[derive(clap::ValueEnum, Debug, Copy, Clone, Serialize, Deserialize)] +enum OverlayType { + Flat, +} + +impl core::fmt::Display for OverlayType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Flat => write!(f, "flat"), + } + } +} + +#[derive(clap::ValueEnum, Debug, Copy, Clone, Serialize, Deserialize)] +enum NodeType { + Carnot, +} + +impl core::fmt::Display for NodeType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Carnot => write!(f, "carnot"), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum OutputType { + File(PathBuf), + StdOut, + StdErr, +} + +impl core::fmt::Display for OutputType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OutputType::File(path) => write!(f, "{}", path.display()), + OutputType::StdOut => write!(f, "stdout"), + OutputType::StdErr => write!(f, "stderr"), + } + } +} + +impl FromStr for OutputType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "stdout" => Ok(Self::StdOut), + "stderr" => Ok(Self::StdErr), + path => Ok(Self::File(PathBuf::from(path))), + } + } +} + +pub fn main() -> Result<(), Box> { + let Args { + config, + overlay_type, + node_type, + output, + } = Args::parse(); + + let report = match (overlay_type, node_type) { + (OverlayType::Flat, NodeType::Carnot) => { + let cfg = serde_json::from_reader::< + _, + Config< + ::Settings, + ::Settings, + CarnotStep, + >, + >(std::fs::File::open(config)?)?; + #[allow(clippy::unit_arg)] + let overlay = FlatOverlay::new(cfg.overlay_settings); + let node_ids = (0..cfg.node_count).collect::>(); + let mut rng = thread_rng(); + let layout = overlay.layout(&node_ids, &mut rng); + let leaders = overlay.leaders(&node_ids, 1, &mut rng).collect(); + + let carnot_steps: Vec<_> = cfg + .steps + .iter() + .copied() + .map(|step| { + ( + LayoutNodes::Leader, + step, + Box::new(|times: &[StepTime]| *times.iter().max().unwrap()) + as Box StepTime>, + ) + }) + .collect(); + + let mut runner: simulations::runner::ConsensusRunner = + ConsensusRunner::new(&mut rng, layout, leaders, cfg.node_settings); + runner.run(&carnot_steps) + } + }; + + let json = serde_json::to_string_pretty(&report)?; + match output { + OutputType::File(f) => { + use std::{fs::OpenOptions, io::Write}; + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(f)?; + file.write_all(json.as_bytes())?; + } + OutputType::StdOut => println!("{json}"), + OutputType::StdErr => eprintln!("{json}"), + } + Ok(()) } diff --git a/simulations/src/config.rs b/simulations/src/config.rs new file mode 100644 index 00000000..fa7f74d5 --- /dev/null +++ b/simulations/src/config.rs @@ -0,0 +1,18 @@ +use crate::network::regions::Region; +use crate::node::StepTime; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize)] +pub struct Config +where + S: core::str::FromStr, +{ + pub network_behaviors: HashMap<(Region, Region), StepTime>, + pub regions: Vec, + pub overlay_settings: O, + pub node_settings: N, + pub node_count: usize, + pub committee_size: usize, + pub steps: Vec, +} diff --git a/simulations/src/lib.rs b/simulations/src/lib.rs index bde0e784..f538d575 100644 --- a/simulations/src/lib.rs +++ b/simulations/src/lib.rs @@ -1,3 +1,4 @@ +pub mod config; pub mod network; pub mod node; pub mod overlay; diff --git a/simulations/src/network/behaviour.rs b/simulations/src/network/behaviour.rs index cae6784a..aa99ca65 100644 --- a/simulations/src/network/behaviour.rs +++ b/simulations/src/network/behaviour.rs @@ -5,7 +5,7 @@ use rand::Rng; use serde::{Deserialize, Serialize}; // internal -#[derive(Default, Serialize, Deserialize)] +#[derive(Default, Debug, Serialize, Deserialize)] pub struct NetworkBehaviour { pub delay: Duration, pub drop: f64, diff --git a/simulations/src/network/mod.rs b/simulations/src/network/mod.rs index fc781f9b..3f65cd1c 100644 --- a/simulations/src/network/mod.rs +++ b/simulations/src/network/mod.rs @@ -8,6 +8,7 @@ use crate::node::NodeId; pub mod behaviour; pub mod regions; +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Network { pub regions: regions::RegionsData, } diff --git a/simulations/src/network/regions.rs b/simulations/src/network/regions.rs index 83301f80..5f9fc120 100644 --- a/simulations/src/network/regions.rs +++ b/simulations/src/network/regions.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; // internal use crate::{network::behaviour::NetworkBehaviour, node::NodeId}; -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum Region { NorthAmerica, Europe, @@ -15,7 +15,7 @@ pub enum Region { Australia, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct RegionsData { pub regions: HashMap>, #[serde(skip)] diff --git a/simulations/src/node/carnot.rs b/simulations/src/node/carnot.rs index 233bbb55..996b9d3a 100644 --- a/simulations/src/node/carnot.rs +++ b/simulations/src/node/carnot.rs @@ -49,7 +49,7 @@ fn receive_commit( .unwrap() } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] pub enum CarnotStep { ReceiveProposal, ValidateProposal, @@ -57,6 +57,20 @@ pub enum CarnotStep { ValidateVote, } +impl core::str::FromStr for CarnotStep { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.trim().to_lowercase().as_str() { + "receiveproposal" => Ok(Self::ReceiveProposal), + "validateproposal" => Ok(Self::ValidateProposal), + "receivevote" => Ok(Self::ReceiveVote), + "validatevote" => Ok(Self::ValidateVote), + _ => Err(format!("Unknown step: {s}")), + } + } +} + #[derive(Clone)] pub enum CarnotStepSolver { Plain(StepTime), @@ -64,8 +78,30 @@ pub enum CarnotStepSolver { ChildCommitteeReceiverSolver(ChildCommitteeReceiverSolver), } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +pub enum CarnotStepSolverType { + Plain(StepTime), + ParentCommitteeReceiverSolver, + ChildCommitteeReceiverSolver, +} + +impl CarnotStepSolverType { + pub fn to_solver(self) -> CarnotStepSolver { + match self { + Self::Plain(time) => CarnotStepSolver::Plain(time), + Self::ParentCommitteeReceiverSolver => { + CarnotStepSolver::ParentCommitteeReceiverSolver(receive_proposal) + } + Self::ChildCommitteeReceiverSolver => { + CarnotStepSolver::ChildCommitteeReceiverSolver(receive_commit) + } + } + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct CarnotNodeSettings { - pub steps_costs: HashMap, + pub steps_costs: HashMap, pub network: Network, pub layout: Layout, } @@ -76,22 +112,22 @@ pub struct CarnotNode { settings: Rc, } -pub const CARNOT_STEPS_COSTS: &[(CarnotStep, CarnotStepSolver)] = &[ +pub const CARNOT_STEPS_COSTS: &[(CarnotStep, CarnotStepSolverType)] = &[ ( CarnotStep::ReceiveProposal, - CarnotStepSolver::ParentCommitteeReceiverSolver(receive_proposal), + CarnotStepSolverType::ParentCommitteeReceiverSolver, ), ( CarnotStep::ValidateProposal, - CarnotStepSolver::Plain(StepTime::from_secs(1)), + CarnotStepSolverType::Plain(StepTime::from_secs(1)), ), ( CarnotStep::ReceiveVote, - CarnotStepSolver::ChildCommitteeReceiverSolver(receive_commit), + CarnotStepSolverType::ChildCommitteeReceiverSolver, ), ( CarnotStep::ValidateVote, - CarnotStepSolver::Plain(StepTime::from_secs(1)), + CarnotStepSolverType::Plain(StepTime::from_secs(1)), ), ]; @@ -130,27 +166,27 @@ impl Node for CarnotNode { fn run_step(&mut self, step: Self::Step) -> StepTime { use CarnotStepSolver::*; match self.settings.steps_costs.get(&step) { - Some(Plain(t)) => *t, - Some(ParentCommitteeReceiverSolver(solver)) => solver( - &mut self.rng, - self.id, - self.settings - .layout - .parent_nodes(self.settings.layout.committee(self.id)), - &self.settings.network, - ), - Some(ChildCommitteeReceiverSolver(solver)) => solver( - &mut self.rng, - self.id, - &self - .settings - .layout - .children_nodes(self.settings.layout.committee(self.id)), - &self.settings.network, - ), - None => { - panic!("Unknown step: {step:?}"); - } + Some(step) => match step.to_solver() { + Plain(t) => t, + ParentCommitteeReceiverSolver(solver) => solver( + &mut self.rng, + self.id, + self.settings + .layout + .parent_nodes(self.settings.layout.committee(self.id)), + &self.settings.network, + ), + ChildCommitteeReceiverSolver(solver) => solver( + &mut self.rng, + self.id, + &self + .settings + .layout + .children_nodes(self.settings.layout.committee(self.id)), + &self.settings.network, + ), + }, + None => panic!("Unknown step: {step:?}"), } } } diff --git a/simulations/src/overlay/mod.rs b/simulations/src/overlay/mod.rs index ec88db45..fc594655 100644 --- a/simulations/src/overlay/mod.rs +++ b/simulations/src/overlay/mod.rs @@ -10,6 +10,7 @@ use crate::node::{CommitteeId, NodeId}; pub type Committee = BTreeSet; pub type Leaders = BTreeSet; +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Layout { pub committees: HashMap, pub from_committee: HashMap, @@ -73,6 +74,7 @@ impl Layout { pub trait Overlay { type Settings; + fn new(settings: Self::Settings) -> Self; fn leaders( &self, diff --git a/simulations/src/runner.rs b/simulations/src/runner.rs index e823eec8..64653e5d 100644 --- a/simulations/src/runner.rs +++ b/simulations/src/runner.rs @@ -14,7 +14,7 @@ pub struct ConsensusRunner { } #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, serde::Serialize)] pub struct Report { round_time: Duration, }