[WIP] Simulation app barebones (#90)
* Add simulations crate * Kickstart modules * Add overlay * Use node steps * Add initial carnot node * Implement Carnot node costs simulation * Firs working runner * Cleanup * Extract overlay from runner * Fix off id calls * Fix testing values * Clippy happy * make simulation app compile to wasm --------- Co-authored-by: al8n <scygliu1@gmail.com>
This commit is contained in:
parent
274e8d55fd
commit
6ce9fdf553
|
@ -9,5 +9,6 @@ members = [
|
|||
"nomos-services/mempool",
|
||||
"nomos-services/http",
|
||||
"nodes/nomos-node",
|
||||
"nodes/mockpool-node"
|
||||
"nodes/mockpool-node",
|
||||
"simulations"
|
||||
]
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "simulations"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
pub fn main() {
|
||||
println!("WIP");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod network;
|
||||
pub mod node;
|
||||
pub mod overlay;
|
||||
pub mod runner;
|
|
@ -0,0 +1,26 @@
|
|||
// std
|
||||
use std::time::Duration;
|
||||
// crates
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// internal
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct NetworkBehaviour {
|
||||
pub delay: Duration,
|
||||
pub drop: f64,
|
||||
}
|
||||
|
||||
impl NetworkBehaviour {
|
||||
pub fn new(delay: Duration, drop: f64) -> Self {
|
||||
Self { delay, drop }
|
||||
}
|
||||
|
||||
pub fn delay(&self) -> Duration {
|
||||
self.delay
|
||||
}
|
||||
|
||||
pub fn should_drop<R: Rng>(&self, rng: &mut R) -> bool {
|
||||
rng.gen_bool(self.drop)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// std
|
||||
use std::time::Duration;
|
||||
// crates
|
||||
use rand::Rng;
|
||||
// internal
|
||||
use crate::node::NodeId;
|
||||
|
||||
pub mod behaviour;
|
||||
pub mod regions;
|
||||
|
||||
pub struct Network {
|
||||
pub regions: regions::RegionsData,
|
||||
}
|
||||
|
||||
impl Network {
|
||||
pub fn new(regions: regions::RegionsData) -> Self {
|
||||
Self { regions }
|
||||
}
|
||||
|
||||
pub fn send_message_cost<R: Rng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
node_a: NodeId,
|
||||
node_b: NodeId,
|
||||
) -> Option<Duration> {
|
||||
let network_behaviour = self.regions.network_behaviour(node_a, node_b);
|
||||
(!network_behaviour.should_drop(rng))
|
||||
// TODO: use a delay range
|
||||
.then(|| network_behaviour.delay())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// std
|
||||
use std::collections::HashMap;
|
||||
// crates
|
||||
use serde::{Deserialize, Serialize};
|
||||
// internal
|
||||
use crate::{network::behaviour::NetworkBehaviour, node::NodeId};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum Region {
|
||||
NorthAmerica,
|
||||
Europe,
|
||||
Asia,
|
||||
Africa,
|
||||
SouthAmerica,
|
||||
Australia,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RegionsData {
|
||||
pub regions: HashMap<Region, Vec<NodeId>>,
|
||||
#[serde(skip)]
|
||||
pub node_region: HashMap<NodeId, Region>,
|
||||
pub region_network_behaviour: HashMap<(Region, Region), NetworkBehaviour>,
|
||||
}
|
||||
|
||||
impl RegionsData {
|
||||
pub fn new(
|
||||
regions: HashMap<Region, Vec<NodeId>>,
|
||||
region_network_behaviour: HashMap<(Region, Region), NetworkBehaviour>,
|
||||
) -> Self {
|
||||
let node_region = regions
|
||||
.iter()
|
||||
.flat_map(|(region, nodes)| nodes.iter().copied().map(|node| (node, *region)))
|
||||
.collect();
|
||||
Self {
|
||||
regions,
|
||||
node_region,
|
||||
region_network_behaviour,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_region(&self, node_id: NodeId) -> Region {
|
||||
self.node_region[&node_id]
|
||||
}
|
||||
|
||||
pub fn network_behaviour(&self, node_a: NodeId, node_b: NodeId) -> &NetworkBehaviour {
|
||||
let region_a = self.node_region[&node_a];
|
||||
let region_b = self.node_region[&node_b];
|
||||
self.region_network_behaviour
|
||||
.get(&(region_a, region_b))
|
||||
.or(self.region_network_behaviour.get(&(region_b, region_a)))
|
||||
.expect("Network behaviour not found for the given regions")
|
||||
}
|
||||
|
||||
pub fn region_nodes(&self, region: Region) -> &[NodeId] {
|
||||
&self.regions[®ion]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
// std
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
// crates
|
||||
use rand::prelude::SmallRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
// internal
|
||||
use crate::network::Network;
|
||||
use crate::node::{Node, NodeId, StepTime};
|
||||
use crate::overlay::{Committee, Layout};
|
||||
|
||||
pub type ParentCommitteeReceiverSolver =
|
||||
fn(&mut SmallRng, NodeId, &Committee, &Network) -> StepTime;
|
||||
|
||||
pub type ChildCommitteeReceiverSolver =
|
||||
fn(&mut SmallRng, NodeId, &[&Committee], &Network) -> StepTime;
|
||||
|
||||
fn receive_proposal(
|
||||
rng: &mut SmallRng,
|
||||
node: NodeId,
|
||||
committee: &Committee,
|
||||
network: &Network,
|
||||
) -> StepTime {
|
||||
assert!(!committee.is_empty());
|
||||
committee
|
||||
.iter()
|
||||
.filter_map(|&sender| network.send_message_cost(rng, sender, node))
|
||||
.max()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn receive_commit(
|
||||
rng: &mut SmallRng,
|
||||
node: NodeId,
|
||||
committees: &[&Committee],
|
||||
network: &Network,
|
||||
) -> StepTime {
|
||||
assert!(!committees.is_empty());
|
||||
committees
|
||||
.iter()
|
||||
.filter_map(|committee| {
|
||||
committee
|
||||
.iter()
|
||||
.filter_map(|&sender| network.send_message_cost(rng, sender, node))
|
||||
.max()
|
||||
})
|
||||
.max()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum CarnotStep {
|
||||
ReceiveProposal,
|
||||
ValidateProposal,
|
||||
ReceiveVote,
|
||||
ValidateVote,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CarnotStepSolver {
|
||||
Plain(StepTime),
|
||||
ParentCommitteeReceiverSolver(ParentCommitteeReceiverSolver),
|
||||
ChildCommitteeReceiverSolver(ChildCommitteeReceiverSolver),
|
||||
}
|
||||
|
||||
pub struct CarnotNodeSettings {
|
||||
pub steps_costs: HashMap<CarnotStep, CarnotStepSolver>,
|
||||
pub network: Network,
|
||||
pub layout: Layout,
|
||||
}
|
||||
|
||||
pub struct CarnotNode {
|
||||
id: NodeId,
|
||||
rng: SmallRng,
|
||||
settings: Rc<CarnotNodeSettings>,
|
||||
}
|
||||
|
||||
pub const CARNOT_STEPS_COSTS: &[(CarnotStep, CarnotStepSolver)] = &[
|
||||
(
|
||||
CarnotStep::ReceiveProposal,
|
||||
CarnotStepSolver::ParentCommitteeReceiverSolver(receive_proposal),
|
||||
),
|
||||
(
|
||||
CarnotStep::ValidateProposal,
|
||||
CarnotStepSolver::Plain(StepTime::from_secs(1)),
|
||||
),
|
||||
(
|
||||
CarnotStep::ReceiveVote,
|
||||
CarnotStepSolver::ChildCommitteeReceiverSolver(receive_commit),
|
||||
),
|
||||
(
|
||||
CarnotStep::ValidateVote,
|
||||
CarnotStepSolver::Plain(StepTime::from_secs(1)),
|
||||
),
|
||||
];
|
||||
|
||||
pub const CARNOT_LEADER_STEPS: &[CarnotStep] = &[CarnotStep::ReceiveVote, CarnotStep::ValidateVote];
|
||||
|
||||
pub const CARNOT_ROOT_STEPS: &[CarnotStep] = &[
|
||||
CarnotStep::ReceiveProposal,
|
||||
CarnotStep::ValidateProposal,
|
||||
CarnotStep::ReceiveVote,
|
||||
CarnotStep::ValidateVote,
|
||||
];
|
||||
|
||||
pub const CARNOT_INTERMEDIATE_STEPS: &[CarnotStep] = &[
|
||||
CarnotStep::ReceiveProposal,
|
||||
CarnotStep::ValidateProposal,
|
||||
CarnotStep::ReceiveVote,
|
||||
CarnotStep::ValidateVote,
|
||||
];
|
||||
|
||||
pub const CARNOT_LEAF_STEPS: &[CarnotStep] =
|
||||
&[CarnotStep::ReceiveProposal, CarnotStep::ValidateProposal];
|
||||
|
||||
impl Node for CarnotNode {
|
||||
type Settings = Rc<CarnotNodeSettings>;
|
||||
type Step = CarnotStep;
|
||||
|
||||
fn new<R: Rng>(rng: &mut R, id: NodeId, settings: Self::Settings) -> Self {
|
||||
let rng = SmallRng::from_rng(rng).unwrap();
|
||||
Self { id, settings, rng }
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
pub mod carnot;
|
||||
|
||||
// std
|
||||
use std::time::Duration;
|
||||
// crates
|
||||
use rand::Rng;
|
||||
// internal
|
||||
|
||||
pub type NodeId = usize;
|
||||
pub type CommitteeId = usize;
|
||||
pub type StepTime = Duration;
|
||||
|
||||
pub trait Node {
|
||||
type Settings;
|
||||
type Step;
|
||||
fn new<R: Rng>(rng: &mut R, id: NodeId, settings: Self::Settings) -> Self;
|
||||
fn id(&self) -> NodeId;
|
||||
fn run_step(&mut self, steps: Self::Step) -> StepTime;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
use rand::prelude::IteratorRandom;
|
||||
use rand::Rng;
|
||||
// std
|
||||
// crates
|
||||
// internal
|
||||
use super::Overlay;
|
||||
use crate::node::NodeId;
|
||||
use crate::overlay::{Committee, Layout};
|
||||
|
||||
pub struct FlatOverlay;
|
||||
|
||||
impl Overlay for FlatOverlay {
|
||||
type Settings = ();
|
||||
|
||||
fn new(_settings: Self::Settings) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn leaders<R: Rng>(
|
||||
&self,
|
||||
nodes: &[NodeId],
|
||||
size: usize,
|
||||
rng: &mut R,
|
||||
) -> Box<dyn Iterator<Item = NodeId>> {
|
||||
let leaders = nodes.iter().copied().choose_multiple(rng, size).into_iter();
|
||||
Box::new(leaders)
|
||||
}
|
||||
|
||||
fn layout<R: Rng>(&self, nodes: &[NodeId], _rng: &mut R) -> Layout {
|
||||
let committees =
|
||||
std::iter::once((0, nodes.iter().copied().collect::<Committee>())).collect();
|
||||
let parent = std::iter::once((0, 0)).collect();
|
||||
let children = std::iter::once((0, vec![0])).collect();
|
||||
Layout::new(committees, parent, children)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
pub mod flat;
|
||||
|
||||
// std
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
// crates
|
||||
use rand::Rng;
|
||||
// internal
|
||||
use crate::node::{CommitteeId, NodeId};
|
||||
|
||||
pub type Committee = BTreeSet<NodeId>;
|
||||
pub type Leaders = BTreeSet<NodeId>;
|
||||
|
||||
pub struct Layout {
|
||||
pub committees: HashMap<CommitteeId, Committee>,
|
||||
pub from_committee: HashMap<NodeId, CommitteeId>,
|
||||
pub parent: HashMap<CommitteeId, CommitteeId>,
|
||||
pub children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn new(
|
||||
committees: HashMap<CommitteeId, Committee>,
|
||||
parent: HashMap<CommitteeId, CommitteeId>,
|
||||
children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||
) -> Self {
|
||||
let from_committee = committees
|
||||
.iter()
|
||||
.flat_map(|(&committee_id, committee)| {
|
||||
committee
|
||||
.iter()
|
||||
.map(move |&node_id| (node_id, committee_id))
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
committees,
|
||||
from_committee,
|
||||
parent,
|
||||
children,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn committee(&self, node_id: NodeId) -> CommitteeId {
|
||||
self.from_committee.get(&node_id).copied().unwrap()
|
||||
}
|
||||
|
||||
pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee {
|
||||
&self.committees[&committee_id]
|
||||
}
|
||||
|
||||
pub fn parent(&self, committee_id: CommitteeId) -> CommitteeId {
|
||||
self.parent[&committee_id]
|
||||
}
|
||||
|
||||
pub fn parent_nodes(&self, committee_id: CommitteeId) -> &Committee {
|
||||
&self.committees[&self.parent(committee_id)]
|
||||
}
|
||||
|
||||
pub fn children(&self, committee_id: CommitteeId) -> &[CommitteeId] {
|
||||
&self.children[&committee_id]
|
||||
}
|
||||
|
||||
pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> {
|
||||
self.children(committee_id)
|
||||
.iter()
|
||||
.map(|&committee_id| &self.committees[&committee_id])
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
|
||||
self.from_committee.keys().copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Overlay {
|
||||
type Settings;
|
||||
fn new(settings: Self::Settings) -> Self;
|
||||
fn leaders<R: Rng>(
|
||||
&self,
|
||||
nodes: &[NodeId],
|
||||
size: usize,
|
||||
rng: &mut R,
|
||||
) -> Box<dyn Iterator<Item = NodeId>>;
|
||||
fn layout<R: Rng>(&self, nodes: &[NodeId], rng: &mut R) -> Layout;
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
// 1 - Leader forwards a proposal - Leader builds proposal
|
||||
// 2 - Every committee member receives the proposal and validates it
|
||||
//
|
||||
|
||||
use crate::node::{Node, NodeId, StepTime};
|
||||
use crate::overlay::Layout;
|
||||
use rand::Rng;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct ConsensusRunner<N> {
|
||||
nodes: Vec<N>,
|
||||
layout: Layout,
|
||||
leaders: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct Report {
|
||||
round_time: Duration,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum LayoutNodes {
|
||||
Leader,
|
||||
Committee,
|
||||
LeafCommittee,
|
||||
}
|
||||
|
||||
pub type ExecutionSteps<S> = [(LayoutNodes, S, Box<dyn Fn(&[StepTime]) -> StepTime>)];
|
||||
|
||||
impl<N: Node> ConsensusRunner<N>
|
||||
where
|
||||
N::Settings: Clone,
|
||||
{
|
||||
pub fn new<R: Rng>(
|
||||
mut rng: R,
|
||||
layout: Layout,
|
||||
leaders: Vec<NodeId>,
|
||||
node_settings: N::Settings,
|
||||
) -> Self {
|
||||
let nodes = layout
|
||||
.node_ids()
|
||||
.map(|id| N::new(&mut rng, id, node_settings.clone()))
|
||||
.collect();
|
||||
Self {
|
||||
nodes,
|
||||
layout,
|
||||
leaders,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
|
||||
self.nodes.iter().map(Node::id)
|
||||
}
|
||||
|
||||
pub fn run(&mut self, execution: &ExecutionSteps<N::Step>) -> Report
|
||||
where
|
||||
N::Step: Clone,
|
||||
{
|
||||
let leaders = &self.leaders;
|
||||
let layout = &self.layout;
|
||||
|
||||
let round_time = execution
|
||||
.iter()
|
||||
.map(|(layout_node, step, reducer)| {
|
||||
let times: Vec<StepTime> = match layout_node {
|
||||
LayoutNodes::Leader => leaders
|
||||
.iter()
|
||||
.map(|&leader| self.nodes[leader].run_step(step.clone()))
|
||||
.collect(),
|
||||
LayoutNodes::Committee => {
|
||||
let non_leaf_committees = layout
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|(id, children)| (!children.is_empty()).then_some(id));
|
||||
|
||||
non_leaf_committees
|
||||
.flat_map(|committee_id| {
|
||||
layout
|
||||
.committees
|
||||
.get(committee_id)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|&node| self.nodes[node].run_step(step.clone()))
|
||||
.max()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
LayoutNodes::LeafCommittee => {
|
||||
let leaf_committees =
|
||||
layout.children.iter().filter_map(|(id, children)| {
|
||||
(children.is_empty() || (layout.parent(*id) == *id)).then_some(id)
|
||||
});
|
||||
|
||||
leaf_committees
|
||||
.flat_map(|committee_id| {
|
||||
layout
|
||||
.committees
|
||||
.get(committee_id)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|&node| self.nodes[node].run_step(step.clone()))
|
||||
.max()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
reducer(×)
|
||||
})
|
||||
.sum();
|
||||
|
||||
Report { round_time }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::network::behaviour::NetworkBehaviour;
|
||||
use crate::network::regions::{Region, RegionsData};
|
||||
use crate::network::Network;
|
||||
use crate::node::carnot::{
|
||||
CarnotNode, CarnotNodeSettings, CARNOT_LEADER_STEPS, CARNOT_LEAF_STEPS, CARNOT_STEPS_COSTS,
|
||||
};
|
||||
use crate::node::{NodeId, StepTime};
|
||||
use crate::overlay::flat::FlatOverlay;
|
||||
use crate::overlay::Overlay;
|
||||
use crate::runner::{ConsensusRunner, LayoutNodes};
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
fn setup_runner<R: Rng, O: Overlay>(
|
||||
mut rng: &mut R,
|
||||
overlay: &O,
|
||||
) -> ConsensusRunner<CarnotNode> {
|
||||
let regions = std::iter::once((Region::Europe, (0..10).collect())).collect();
|
||||
let network_behaviour = std::iter::once((
|
||||
(Region::Europe, Region::Europe),
|
||||
NetworkBehaviour::new(Duration::from_millis(100), 0.0),
|
||||
))
|
||||
.collect();
|
||||
let node_ids: Vec<NodeId> = (0..10).collect();
|
||||
let layout = overlay.layout(&node_ids, &mut rng);
|
||||
let leaders = overlay.leaders(&node_ids, 1, &mut rng).collect();
|
||||
let node_settings: CarnotNodeSettings = CarnotNodeSettings {
|
||||
steps_costs: CARNOT_STEPS_COSTS.iter().cloned().collect(),
|
||||
network: Network::new(RegionsData::new(regions, network_behaviour)),
|
||||
layout: overlay.layout(&node_ids, &mut rng),
|
||||
};
|
||||
|
||||
ConsensusRunner::new(&mut rng, layout, leaders, Rc::new(node_settings))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_flat_single_leader_steps() {
|
||||
let mut rng = SmallRng::seed_from_u64(0);
|
||||
let overlay = FlatOverlay::new(());
|
||||
|
||||
let mut runner = setup_runner(&mut rng, &overlay);
|
||||
|
||||
let carnot_steps: Vec<_> = CARNOT_LEADER_STEPS
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|step| {
|
||||
(
|
||||
LayoutNodes::Leader,
|
||||
step,
|
||||
Box::new(|times: &[StepTime]| *times.iter().max().unwrap())
|
||||
as Box<dyn Fn(&[StepTime]) -> StepTime>,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
Duration::from_millis(1100),
|
||||
runner.run(&carnot_steps).round_time
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_flat_single_leader_single_committee() {
|
||||
let mut rng = SmallRng::seed_from_u64(0);
|
||||
let overlay = FlatOverlay::new(());
|
||||
|
||||
let mut runner: ConsensusRunner<CarnotNode> = setup_runner(&mut rng, &overlay);
|
||||
|
||||
let leader_steps = CARNOT_LEADER_STEPS.iter().copied().map(|step| {
|
||||
(
|
||||
LayoutNodes::Leader,
|
||||
step,
|
||||
Box::new(|times: &[StepTime]| *times.iter().max().unwrap())
|
||||
as Box<dyn Fn(&[StepTime]) -> StepTime>,
|
||||
)
|
||||
});
|
||||
|
||||
let committee_steps = CARNOT_LEAF_STEPS.iter().copied().map(|step| {
|
||||
(
|
||||
LayoutNodes::LeafCommittee,
|
||||
step,
|
||||
Box::new(|times: &[StepTime]| *times.iter().max().unwrap())
|
||||
as Box<dyn Fn(&[StepTime]) -> StepTime>,
|
||||
)
|
||||
});
|
||||
|
||||
let carnot_steps: Vec<_> = leader_steps.chain(committee_steps).collect();
|
||||
|
||||
assert_eq!(
|
||||
Duration::from_millis(2200),
|
||||
runner.run(&carnot_steps).round_time
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue