Add configuration for simulation app (#94)
* make simulation app compile to wasm * add configuration * improve args parsing * add steps in configuration --------- Co-authored-by: Daniel Sanchez Quiros <sanchez.quiros.daniel@gmail.com>
This commit is contained in:
parent
1ec4231a7a
commit
8a1af8c234
|
@ -113,7 +113,7 @@ impl HttpBackend for AxumBackend {
|
|||
let router = self.router.clone();
|
||||
let service = tower::service_fn(move |request: Request<Body>| {
|
||||
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<HttpRequest>) {
|
||||
let handle_data = |Query(query): Query<HashMap<String, String>>, payload: Option<Bytes>| async move {
|
||||
handle_req(req_stream, query, payload).await
|
||||
let handle_data = |Query(query): Query<HashMap<String, String>>, payload: Option<Bytes>| {
|
||||
handle_req(req_stream, query, payload)
|
||||
};
|
||||
|
||||
let handler = match method {
|
||||
|
|
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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<Self, Self::Err> {
|
||||
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<dyn std::error::Error>> {
|
||||
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<
|
||||
<CarnotNode as Node>::Settings,
|
||||
<FlatOverlay as Overlay>::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::<Vec<_>>();
|
||||
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<dyn Fn(&[StepTime]) -> StepTime>,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut runner: simulations::runner::ConsensusRunner<CarnotNode> =
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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<N, O, S>
|
||||
where
|
||||
S: core::str::FromStr,
|
||||
{
|
||||
pub network_behaviors: HashMap<(Region, Region), StepTime>,
|
||||
pub regions: Vec<Region>,
|
||||
pub overlay_settings: O,
|
||||
pub node_settings: N,
|
||||
pub node_count: usize,
|
||||
pub committee_size: usize,
|
||||
pub steps: Vec<S>,
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod config;
|
||||
pub mod network;
|
||||
pub mod node;
|
||||
pub mod overlay;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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<Region, Vec<NodeId>>,
|
||||
#[serde(skip)]
|
||||
|
|
|
@ -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<Self, Self::Err> {
|
||||
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<CarnotStep, CarnotStepSolver>,
|
||||
pub steps_costs: HashMap<CarnotStep, CarnotStepSolverType>,
|
||||
pub network: Network,
|
||||
pub layout: Layout,
|
||||
}
|
||||
|
@ -76,22 +112,22 @@ pub struct CarnotNode {
|
|||
settings: Rc<CarnotNodeSettings>,
|
||||
}
|
||||
|
||||
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:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::node::{CommitteeId, NodeId};
|
|||
pub type Committee = BTreeSet<NodeId>;
|
||||
pub type Leaders = BTreeSet<NodeId>;
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Layout {
|
||||
pub committees: HashMap<CommitteeId, Committee>,
|
||||
pub from_committee: HashMap<NodeId, CommitteeId>,
|
||||
|
@ -73,6 +74,7 @@ impl Layout {
|
|||
|
||||
pub trait Overlay {
|
||||
type Settings;
|
||||
|
||||
fn new(settings: Self::Settings) -> Self;
|
||||
fn leaders<R: Rng>(
|
||||
&self,
|
||||
|
|
|
@ -14,7 +14,7 @@ pub struct ConsensusRunner<N> {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct Report {
|
||||
round_time: Duration,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue