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 router = self.router.clone();
|
||||||
let service = tower::service_fn(move |request: Request<Body>| {
|
let service = tower::service_fn(move |request: Request<Body>| {
|
||||||
let mut router = router.lock().clone();
|
let mut router = router.lock().clone();
|
||||||
async move { router.call(request).await }
|
router.call(request)
|
||||||
});
|
});
|
||||||
|
|
||||||
axum::Server::bind(&self.config.address)
|
axum::Server::bind(&self.config.address)
|
||||||
|
@ -125,8 +125,8 @@ impl HttpBackend for AxumBackend {
|
||||||
|
|
||||||
impl AxumBackend {
|
impl AxumBackend {
|
||||||
fn add_data_route(&self, method: HttpMethod, path: &str, req_stream: Sender<HttpRequest>) {
|
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 {
|
let handle_data = |Query(query): Query<HashMap<String, String>>, payload: Option<Bytes>| {
|
||||||
handle_req(req_stream, query, payload).await
|
handle_req(req_stream, query, payload)
|
||||||
};
|
};
|
||||||
|
|
||||||
let handler = match method {
|
let handler = match method {
|
||||||
|
|
|
@ -7,9 +7,11 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2", features = ["js"] }
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,146 @@
|
||||||
pub fn main() {
|
use std::{path::PathBuf, str::FromStr};
|
||||||
println!("WIP");
|
|
||||||
|
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 network;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod overlay;
|
pub mod overlay;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// internal
|
// internal
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Debug, Serialize, Deserialize)]
|
||||||
pub struct NetworkBehaviour {
|
pub struct NetworkBehaviour {
|
||||||
pub delay: Duration,
|
pub delay: Duration,
|
||||||
pub drop: f64,
|
pub drop: f64,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::node::NodeId;
|
||||||
pub mod behaviour;
|
pub mod behaviour;
|
||||||
pub mod regions;
|
pub mod regions;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Network {
|
pub struct Network {
|
||||||
pub regions: regions::RegionsData,
|
pub regions: regions::RegionsData,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||||
// internal
|
// internal
|
||||||
use crate::{network::behaviour::NetworkBehaviour, node::NodeId};
|
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 {
|
pub enum Region {
|
||||||
NorthAmerica,
|
NorthAmerica,
|
||||||
Europe,
|
Europe,
|
||||||
|
@ -15,7 +15,7 @@ pub enum Region {
|
||||||
Australia,
|
Australia,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RegionsData {
|
pub struct RegionsData {
|
||||||
pub regions: HashMap<Region, Vec<NodeId>>,
|
pub regions: HashMap<Region, Vec<NodeId>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
|
|
@ -49,7 +49,7 @@ fn receive_commit(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum CarnotStep {
|
pub enum CarnotStep {
|
||||||
ReceiveProposal,
|
ReceiveProposal,
|
||||||
ValidateProposal,
|
ValidateProposal,
|
||||||
|
@ -57,6 +57,20 @@ pub enum CarnotStep {
|
||||||
ValidateVote,
|
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)]
|
#[derive(Clone)]
|
||||||
pub enum CarnotStepSolver {
|
pub enum CarnotStepSolver {
|
||||||
Plain(StepTime),
|
Plain(StepTime),
|
||||||
|
@ -64,8 +78,30 @@ pub enum CarnotStepSolver {
|
||||||
ChildCommitteeReceiverSolver(ChildCommitteeReceiverSolver),
|
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 struct CarnotNodeSettings {
|
||||||
pub steps_costs: HashMap<CarnotStep, CarnotStepSolver>,
|
pub steps_costs: HashMap<CarnotStep, CarnotStepSolverType>,
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
pub layout: Layout,
|
pub layout: Layout,
|
||||||
}
|
}
|
||||||
|
@ -76,22 +112,22 @@ pub struct CarnotNode {
|
||||||
settings: Rc<CarnotNodeSettings>,
|
settings: Rc<CarnotNodeSettings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CARNOT_STEPS_COSTS: &[(CarnotStep, CarnotStepSolver)] = &[
|
pub const CARNOT_STEPS_COSTS: &[(CarnotStep, CarnotStepSolverType)] = &[
|
||||||
(
|
(
|
||||||
CarnotStep::ReceiveProposal,
|
CarnotStep::ReceiveProposal,
|
||||||
CarnotStepSolver::ParentCommitteeReceiverSolver(receive_proposal),
|
CarnotStepSolverType::ParentCommitteeReceiverSolver,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
CarnotStep::ValidateProposal,
|
CarnotStep::ValidateProposal,
|
||||||
CarnotStepSolver::Plain(StepTime::from_secs(1)),
|
CarnotStepSolverType::Plain(StepTime::from_secs(1)),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
CarnotStep::ReceiveVote,
|
CarnotStep::ReceiveVote,
|
||||||
CarnotStepSolver::ChildCommitteeReceiverSolver(receive_commit),
|
CarnotStepSolverType::ChildCommitteeReceiverSolver,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
CarnotStep::ValidateVote,
|
CarnotStep::ValidateVote,
|
||||||
CarnotStepSolver::Plain(StepTime::from_secs(1)),
|
CarnotStepSolverType::Plain(StepTime::from_secs(1)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -130,8 +166,9 @@ impl Node for CarnotNode {
|
||||||
fn run_step(&mut self, step: Self::Step) -> StepTime {
|
fn run_step(&mut self, step: Self::Step) -> StepTime {
|
||||||
use CarnotStepSolver::*;
|
use CarnotStepSolver::*;
|
||||||
match self.settings.steps_costs.get(&step) {
|
match self.settings.steps_costs.get(&step) {
|
||||||
Some(Plain(t)) => *t,
|
Some(step) => match step.to_solver() {
|
||||||
Some(ParentCommitteeReceiverSolver(solver)) => solver(
|
Plain(t) => t,
|
||||||
|
ParentCommitteeReceiverSolver(solver) => solver(
|
||||||
&mut self.rng,
|
&mut self.rng,
|
||||||
self.id,
|
self.id,
|
||||||
self.settings
|
self.settings
|
||||||
|
@ -139,7 +176,7 @@ impl Node for CarnotNode {
|
||||||
.parent_nodes(self.settings.layout.committee(self.id)),
|
.parent_nodes(self.settings.layout.committee(self.id)),
|
||||||
&self.settings.network,
|
&self.settings.network,
|
||||||
),
|
),
|
||||||
Some(ChildCommitteeReceiverSolver(solver)) => solver(
|
ChildCommitteeReceiverSolver(solver) => solver(
|
||||||
&mut self.rng,
|
&mut self.rng,
|
||||||
self.id,
|
self.id,
|
||||||
&self
|
&self
|
||||||
|
@ -148,9 +185,8 @@ impl Node for CarnotNode {
|
||||||
.children_nodes(self.settings.layout.committee(self.id)),
|
.children_nodes(self.settings.layout.committee(self.id)),
|
||||||
&self.settings.network,
|
&self.settings.network,
|
||||||
),
|
),
|
||||||
None => {
|
},
|
||||||
panic!("Unknown step: {step:?}");
|
None => panic!("Unknown step: {step:?}"),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::node::{CommitteeId, NodeId};
|
||||||
pub type Committee = BTreeSet<NodeId>;
|
pub type Committee = BTreeSet<NodeId>;
|
||||||
pub type Leaders = BTreeSet<NodeId>;
|
pub type Leaders = BTreeSet<NodeId>;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub committees: HashMap<CommitteeId, Committee>,
|
pub committees: HashMap<CommitteeId, Committee>,
|
||||||
pub from_committee: HashMap<NodeId, CommitteeId>,
|
pub from_committee: HashMap<NodeId, CommitteeId>,
|
||||||
|
@ -73,6 +74,7 @@ impl Layout {
|
||||||
|
|
||||||
pub trait Overlay {
|
pub trait Overlay {
|
||||||
type Settings;
|
type Settings;
|
||||||
|
|
||||||
fn new(settings: Self::Settings) -> Self;
|
fn new(settings: Self::Settings) -> Self;
|
||||||
fn leaders<R: Rng>(
|
fn leaders<R: Rng>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub struct ConsensusRunner<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
round_time: Duration,
|
round_time: Duration,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue