[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/mempool",
|
||||||
"nomos-services/http",
|
"nomos-services/http",
|
||||||
"nodes/nomos-node",
|
"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