diff --git a/simulations/src/network/mod.rs b/simulations/src/network/mod.rs index 4a7e2d06..cdbddbc5 100644 --- a/simulations/src/network/mod.rs +++ b/simulations/src/network/mod.rs @@ -80,17 +80,18 @@ where /// delay has passed. pub fn dispatch_after(&mut self, rng: &mut R, time_passed: Duration) { self.network_time += time_passed; + let mut delayed = vec![]; while let Some((network_time, message)) = self.messages.pop() { - // TODO: Handle message drops (remove unwrap). - let delay = self - .send_message_cost(rng, message.from, message.to) - .unwrap(); - if network_time.add(delay) <= self.network_time { - let to_node = self.to_node_senders.get(&message.to).unwrap(); - to_node.send(message).expect("Node should have connection"); - } else { - delayed.push((network_time, message)); + // If cost is None, message won't get sent nor it will be + // readded to the pending messages list. + if let Some(delay) = self.send_message_cost(rng, message.from, message.to) { + if network_time.add(delay) <= self.network_time { + let to_node = self.to_node_senders.get(&message.to).unwrap(); + to_node.send(message).expect("Node should have connection"); + } else { + delayed.push((network_time, message)); + } } } self.messages = delayed; diff --git a/simulations/src/node/dummy.rs b/simulations/src/node/dummy.rs index 93a6b23e..3335a841 100644 --- a/simulations/src/node/dummy.rs +++ b/simulations/src/node/dummy.rs @@ -1,4 +1,5 @@ // std +use std::collections::{BTreeMap, BTreeSet}; // crates use crossbeam::channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; @@ -8,42 +9,343 @@ use crate::{ node::{Node, NodeId}, }; -use super::{NetworkState, SharedState}; +use super::{OverlayGetter, OverlayState, SharedState, ViewOverlay}; #[derive(Debug, Default, Serialize)] pub struct DummyState { pub current_view: usize, + pub message_count: usize, + pub view_state: BTreeMap, +} + +#[derive(Debug, Default, Serialize)] +pub struct DummyViewState { + proposal_received: bool, + vote_received_count: usize, + vote_sent: bool, } #[derive(Clone, Default, Deserialize)] pub struct DummySettings {} -#[derive(Clone)] +/// Helper intent to distinguish between votes ment for different roles in the tree +/// because dummy node does not compute QC. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub enum Intent { + FromRootToLeader, + FromInternalToInternal, + #[default] + FromLeafToInternal, +} + +#[derive(Debug, Clone)] +pub struct Vote { + pub view: usize, + pub intent: Intent, +} + +impl Vote { + pub fn new(id: usize, intent: Intent) -> Self { + Self { view: id, intent } + } + + pub fn upgrade(&self, intent: Intent) -> Self { + Self { + view: self.view, + intent, + } + } +} + +impl From for Vote { + fn from(id: usize) -> Self { + Self { + view: id, + intent: Default::default(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Block { + pub view: usize, +} + +impl Block { + pub fn new(id: usize) -> Self { + Self { view: id } + } +} + +impl From for Block { + fn from(id: usize) -> Self { + Self { view: id } + } +} + +#[derive(Debug, Clone)] pub enum DummyMessage { - EventOne(usize), - EventTwo(usize), + Vote(Vote), + Proposal(Block), +} + +struct LocalView { + pub next_view_leaders: Vec, + pub current_roots: Option>, + pub children: Option>, + pub parents: Option>, + pub roles: Vec, +} + +impl LocalView { + pub fn new(node_id: NodeId, view_id: usize, overlays: O) -> Self { + let view = overlays + .get_view(view_id) + .expect("simulation generated enough views"); + let next_view = overlays + .get_view(view_id + 1) + .expect("simulation generated enough views"); + + let parents = get_parent_nodes(node_id, &view); + let children = get_child_nodes(node_id, &view); + let roles = get_roles(node_id, &next_view, &parents, &children); + // In tree layout CommitteeId(0) is always root committee. + let current_roots = view + .layout + .committees + .get(&0.into()) + .map(|c| c.nodes.clone()); + + Self { + next_view_leaders: next_view.leaders, + current_roots, + children, + parents, + roles, + } + } } pub struct DummyNode { node_id: NodeId, state: DummyState, _settings: DummySettings, - _network_state: SharedState, + overlay_state: SharedState, network_interface: DummyNetworkInterface, + local_view: LocalView, + + // Node in current view might be a leader in the next view. + // To prevent two states for different roles colliding, temp_leader_state + // is used only for leaders. It is overridden when current_view is updated. + temp_leader_state: DummyViewState, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DummyRole { + Leader, + Root, + Internal, + Leaf, + Unknown, } impl DummyNode { pub fn new( node_id: NodeId, - _network_state: SharedState, + view_id: usize, + overlay_state: SharedState, network_interface: DummyNetworkInterface, ) -> Self { Self { node_id, - state: DummyState { current_view: 0 }, - _settings: DummySettings {}, - _network_state, + state: Default::default(), + _settings: Default::default(), + overlay_state: overlay_state.clone(), network_interface, + local_view: LocalView::new(node_id, view_id, overlay_state), + temp_leader_state: Default::default(), + } + } + + pub fn send_message(&self, address: NodeId, message: DummyMessage) { + self.network_interface.send_message(address, message); + } + + fn broadcast(&self, addresses: &[NodeId], message: DummyMessage) { + addresses + .iter() + .for_each(|address| self.send_message(*address, message.clone())) + } + + fn update_view(&mut self, view: usize) { + self.state.view_state.insert( + view, + DummyViewState { + proposal_received: true, + ..Default::default() + }, + ); + self.temp_leader_state = Default::default(); + self.state.current_view = view; + self.local_view = LocalView::new(self.id(), view, self.overlay_state.clone()); + } + + fn is_vote_sent(&self, view: usize) -> bool { + self.state + .view_state + .get(&view) + .expect("view state created") + .vote_sent + } + + fn set_vote_sent(&mut self, view: usize) { + let view_state = self + .state + .view_state + .get_mut(&view) + .expect("view state created"); + view_state.vote_sent = true; + } + + fn get_vote_count(&self, view: usize) -> usize { + self.state + .view_state + .get(&view) + .expect("view state created") + .vote_received_count + } + + fn increment_vote_count(&mut self, view: usize) { + let view_state = self + .state + .view_state + .get_mut(&view) + .expect("view state created"); + view_state.vote_received_count += 1; + } + + fn has_enough_votes(&self, count: usize, children: &Option>) -> bool { + let children = children.as_ref().expect("has children"); + // TODO: Get percentage from the node settings. + let enough = children.len() as f32 * 0.66; + + count > enough as usize + } + + // Assumptions: + // - Leader gets vote from root nodes that are in previous overlay. + // - Leader sends NewView message to all it's view nodes if it receives votes from all root + // nodes. + fn handle_leader(&mut self, message: &NetworkMessage) { + if let DummyMessage::Vote(vote) = &message.payload { + // Internal node can be a leader in the next view, check if the vote traversed the + // whole tree. + if vote.intent != Intent::FromRootToLeader || vote.view < self.current_view() { + return; + } + + self.temp_leader_state.vote_received_count += 1; + if !self.temp_leader_state.vote_sent + && self.has_enough_votes( + self.temp_leader_state.vote_received_count, + &self.local_view.current_roots, + ) + { + let new_view_id = self.current_view() + 1; + self.broadcast( + &self.overlay_state.get_all_nodes(), + DummyMessage::Proposal(new_view_id.into()), + ); + self.temp_leader_state.vote_sent = true; + } + } + } + + fn handle_root(&mut self, message: &NetworkMessage) { + if let DummyMessage::Vote(vote) = &message.payload { + // Root node can be a leader in the next view, check if the vote traversed the + // whole tree. + if vote.intent != Intent::FromInternalToInternal || vote.view != self.current_view() { + return; + } + + self.increment_vote_count(vote.view); + if !self.is_vote_sent(vote.view) + && self.has_enough_votes(self.get_vote_count(vote.view), &self.local_view.children) + { + self.broadcast( + &self.local_view.next_view_leaders, + DummyMessage::Vote(vote.upgrade(Intent::FromRootToLeader)), + ); + self.set_vote_sent(vote.view); + } + } + } + + fn handle_internal(&mut self, message: &NetworkMessage) { + if let DummyMessage::Vote(vote) = &message.payload { + // Internal node can be a leader in the next view, check if the vote traversed the + // whole tree. + if vote.intent != Intent::FromLeafToInternal + && vote.intent != Intent::FromInternalToInternal + || vote.view != self.current_view() + { + return; + } + + self.increment_vote_count(vote.view); + if !self.is_vote_sent(vote.view) + && self.has_enough_votes(self.get_vote_count(vote.view), &self.local_view.children) + { + let parents = self + .local_view + .parents + .as_ref() + .expect("internal has parents"); + parents.iter().for_each(|node_id| { + self.send_message( + *node_id, + DummyMessage::Vote(vote.upgrade(Intent::FromInternalToInternal)), + ) + }); + self.set_vote_sent(vote.view); + } + } + } + + fn handle_leaf(&mut self, message: &NetworkMessage) { + if let DummyMessage::Proposal(block) = &message.payload { + if block.view > self.current_view() { + self.update_view(block.view); + } + if !self.is_vote_sent(block.view) { + let parents = &self.local_view.parents.as_ref().expect("leaf has parents"); + parents.iter().for_each(|node_id| { + self.send_message(*node_id, DummyMessage::Vote(block.view.into())) + }); + self.set_vote_sent(block.view); + } + } + } + + fn handle_message(&mut self, message: &NetworkMessage) { + // The view can change on any message, node needs to change its position + // and roles if the view changes during the message processing. + if let DummyMessage::Proposal(block) = &message.payload { + if block.view > self.current_view() { + self.update_view(block.view); + } + } + let roles = self.local_view.roles.clone(); + + for role in roles.iter() { + match role { + DummyRole::Leader => self.handle_leader(message), + DummyRole::Root => self.handle_root(message), + DummyRole::Internal => self.handle_internal(message), + DummyRole::Leaf => self.handle_leaf(message), + DummyRole::Unknown => (), + } } } } @@ -66,14 +368,11 @@ impl Node for DummyNode { fn step(&mut self) { let incoming_messages = self.network_interface.receive_messages(); - self.state.current_view += 1; + self.state.message_count += incoming_messages.len(); - for message in incoming_messages { - match message.payload { - DummyMessage::EventOne(_) => todo!(), - DummyMessage::EventTwo(_) => todo!(), - } - } + incoming_messages + .iter() + .for_each(|m| self.handle_message(m)); } } @@ -109,3 +408,513 @@ impl NetworkInterface for DummyNetworkInterface { self.receiver.try_iter().collect() } } + +fn get_parent_nodes(node_id: NodeId, view: &ViewOverlay) -> Option> { + let committee_id = view.layout.committee(node_id)?; + view.layout.parent_nodes(committee_id).map(|c| c.nodes) +} + +fn get_child_nodes(node_id: NodeId, view: &ViewOverlay) -> Option> { + let committee_id = view.layout.committee(node_id)?; + let child_nodes: BTreeSet = view + .layout + .children_nodes(committee_id) + .iter() + .flat_map(|c| c.nodes.clone()) + .collect(); + match child_nodes.len() { + 0 => None, + _ => Some(child_nodes), + } +} + +fn get_roles( + node_id: NodeId, + next_view: &ViewOverlay, + parents: &Option>, + children: &Option>, +) -> Vec { + let mut roles = Vec::new(); + if next_view.leaders.contains(&node_id) { + roles.push(DummyRole::Leader); + } + + match (parents, children) { + (None, Some(_)) => roles.push(DummyRole::Root), + (Some(_), Some(_)) => roles.push(DummyRole::Internal), + (Some(_), None) => roles.push(DummyRole::Leaf), + (None, None) => { + //roles.push(DummyRole::Root); + //roles.push(DummyRole::Leaf); + roles.push(DummyRole::Unknown); + } + }; + roles +} + +#[cfg(test)] +mod tests { + use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + sync::{Arc, RwLock}, + time::{Duration, SystemTime, UNIX_EPOCH}, + }; + + use crossbeam::channel; + use rand::{ + rngs::{mock::StepRng, SmallRng}, + Rng, SeedableRng, + }; + use rayon::prelude::*; + + use crate::{ + network::{ + behaviour::NetworkBehaviour, + regions::{Region, RegionsData}, + Network, + }, + node::{ + dummy::{get_child_nodes, get_parent_nodes, get_roles, DummyRole}, + Node, NodeId, OverlayState, SharedState, ViewOverlay, + }, + overlay::{ + tree::{TreeOverlay, TreeSettings}, + Overlay, + }, + }; + + use super::{DummyMessage, DummyNetworkInterface, DummyNode, Intent, Vote}; + + fn init_network(node_ids: &[NodeId]) -> Network { + let regions = HashMap::from([(Region::Europe, node_ids.to_vec())]); + let behaviour = HashMap::from([( + (Region::Europe, Region::Europe), + NetworkBehaviour::new(Duration::from_millis(100), 0.0), + )]); + let regions_data = RegionsData::new(regions, behaviour); + Network::new(regions_data) + } + + fn init_dummy_nodes( + node_ids: &[NodeId], + network: &mut Network, + overlay_state: SharedState, + ) -> HashMap { + node_ids + .iter() + .map(|node_id| { + let (node_message_sender, node_message_receiver) = channel::unbounded(); + let network_message_receiver = network.connect(*node_id, node_message_receiver); + let network_interface = DummyNetworkInterface::new( + *node_id, + node_message_sender, + network_message_receiver, + ); + ( + *node_id, + DummyNode::new(*node_id, 0, overlay_state.clone(), network_interface), + ) + }) + .collect() + } + + fn generate_overlays( + node_ids: &[NodeId], + overlay: O, + overlay_count: usize, + leader_count: usize, + rng: &mut R, + ) -> BTreeMap { + (0..overlay_count) + .map(|view_id| { + ( + view_id, + ViewOverlay { + leaders: overlay.leaders(node_ids, leader_count, rng).collect(), + layout: overlay.layout(node_ids, rng), + }, + ) + }) + .collect() + } + + fn send_initial_votes( + overlays: &BTreeMap, + committee_size: usize, + nodes: &HashMap, + ) { + let initial_vote = Vote::new(1, Intent::FromRootToLeader); + overlays + .get(&1) + .unwrap() + .leaders + .iter() + .for_each(|leader_id| { + for _ in 0..committee_size { + nodes + .get(&0.into()) + .unwrap() + .send_message(*leader_id, DummyMessage::Vote(initial_vote.clone())); + } + }); + } + + #[test] + fn send_receive_tree_overlay_steprng() { + let mut rng = StepRng::new(1, 0); + // 0 + // 1 2 + // 3 4 5 6 + let overlay = TreeOverlay::new(TreeSettings { + tree_type: Default::default(), + depth: 3, + committee_size: 1, + }); + let node_ids: Vec = overlay.nodes(); + let mut network = init_network(&node_ids); + + let view = ViewOverlay { + leaders: vec![0.into(), 1.into(), 2.into()], + layout: overlay.layout(&node_ids, &mut rng), + }; + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: BTreeMap::from([ + (0, view.clone()), + (1, view.clone()), + (2, view.clone()), + (3, view), + ]), + })); + + let mut nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); + let initial_vote = Vote::new(1, Intent::FromRootToLeader); + + // Using any node as the sender for initial proposal to leader nodes. + nodes[&0.into()].send_message(0.into(), DummyMessage::Vote(initial_vote.clone())); + nodes[&0.into()].send_message(1.into(), DummyMessage::Vote(initial_vote.clone())); + nodes[&0.into()].send_message(2.into(), DummyMessage::Vote(initial_vote)); + network.collect_messages(); + + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 0); + } + + // 1. Leaders receive vote and broadcast new Proposal(Block) to all nodes. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // 2. a) All nodes received proposal block. + // b) Leaf nodes send vote to internal nodes. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // All nodes should be updated to the proposed blocks view. + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 1); + } + + // Root and Internal haven't sent their votes yet. + assert!(!nodes[&0.into()].state().view_state[&1].vote_sent); // Root + assert!(!nodes[&1.into()].state().view_state[&1].vote_sent); // Internal + assert!(!nodes[&2.into()].state().view_state[&1].vote_sent); // Internal + + // Leaves should have thier vote sent. + assert!(nodes[&3.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&4.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&5.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&6.into()].state().view_state[&1].vote_sent); // Leaf + + // 3. Internal nodes send vote to root node. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // Root hasn't sent its votes yet. + assert!(!nodes[&0.into()].state().view_state[&1].vote_sent); // Root + + // Internal and leaves should have thier vote sent. + assert!(nodes[&1.into()].state().view_state[&1].vote_sent); // Internal + assert!(nodes[&2.into()].state().view_state[&1].vote_sent); // Internal + assert!(nodes[&3.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&4.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&5.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&6.into()].state().view_state[&1].vote_sent); // Leaf + + // 4. Root node send vote to next view leader nodes. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // Root has sent its votes. + assert!(nodes[&0.into()].state().view_state[&1].vote_sent); // Root + assert!(nodes[&1.into()].state().view_state[&1].vote_sent); // Internal + assert!(nodes[&2.into()].state().view_state[&1].vote_sent); // Internal + assert!(nodes[&3.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&4.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&5.into()].state().view_state[&1].vote_sent); // Leaf + assert!(nodes[&6.into()].state().view_state[&1].vote_sent); // Leaf + + // 5. Leaders receive vote and broadcast new Proposal(Block) to all nodes. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // All nodes should be in an old view + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 1); // old + } + + // 6. a) All nodes received proposal block. + // b) Leaf nodes send vote to internal nodes. + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + + // All nodes should be updated to the proposed blocks view. + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 2); // new + } + + // Root and Internal haven't sent their votes yet. + assert!(!nodes[&0.into()].state().view_state[&2].vote_sent); // Root + assert!(!nodes[&1.into()].state().view_state[&2].vote_sent); // Internal + assert!(!nodes[&2.into()].state().view_state[&2].vote_sent); // Internal + + // Leaves should have thier vote sent. + assert!(nodes[&3.into()].state().view_state[&2].vote_sent); // Leaf + assert!(nodes[&4.into()].state().view_state[&2].vote_sent); // Leaf + assert!(nodes[&5.into()].state().view_state[&2].vote_sent); // Leaf + assert!(nodes[&6.into()].state().view_state[&2].vote_sent); // Leaf + } + + #[test] + fn send_receive_tree_overlay_smallrng() { + // Nodes should progress to second view in 6 steps with random nodes in tree overlay if + // network conditions are the same as in `send_receive_tree_overlay` test. + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("valid unix timestamp") + .as_secs(); + let mut rng = SmallRng::seed_from_u64(timestamp); + + let committee_size = 1; + let overlay = TreeOverlay::new(TreeSettings { + tree_type: Default::default(), + depth: 3, + committee_size, + }); + + // There are more nodes in the network than in a tree overlay. + let node_ids: Vec = (0..100).map(Into::into).collect(); + let mut network = init_network(&node_ids); + + let overlays = generate_overlays(&node_ids, overlay, 4, 3, &mut rng); + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: overlays.clone(), + })); + + let mut nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); + + // Using any node as the sender for initial proposal to leader nodes. + send_initial_votes(&overlays, committee_size, &nodes); + network.collect_messages(); + + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 0); + } + + for _ in 0..7 { + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + } + + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 2); + } + } + + #[test] + #[ignore] + fn send_receive_tree_overlay_medium_network() { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("valid unix timestamp") + .as_secs(); + let mut rng = SmallRng::seed_from_u64(timestamp); + + let committee_size = 100; + let overlay = TreeOverlay::new(TreeSettings { + tree_type: Default::default(), + depth: 3, + committee_size, + }); + + // There are more nodes in the network than in a tree overlay. + let node_ids: Vec = (0..10000).map(Into::into).collect(); + let mut network = init_network(&node_ids); + + let overlays = generate_overlays(&node_ids, overlay, 4, 100, &mut rng); + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: overlays.clone(), + })); + + let mut nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); + + // Using any node as the sender for initial proposal to leader nodes. + send_initial_votes(&overlays, committee_size, &nodes); + network.collect_messages(); + + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 0); + } + + for _ in 0..7 { + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + } + + for (_, node) in nodes.iter() { + assert_eq!(node.current_view(), 2); + } + } + + #[test] + #[ignore] + fn send_receive_tree_overlay_large_network() { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("valid unix timestamp") + .as_secs(); + let mut rng = SmallRng::seed_from_u64(timestamp); + + let committee_size = 500; + let overlay = TreeOverlay::new(TreeSettings { + tree_type: Default::default(), + depth: 5, + committee_size, + }); + + // There are more nodes in the network than in a tree overlay. + let node_ids: Vec = (0..100000).map(Into::into).collect(); + let mut network = init_network(&node_ids); + + let overlays = generate_overlays(&node_ids, overlay, 4, 1000, &mut rng); + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: overlays.clone(), + })); + + let nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); + + // Using any node as the sender for initial proposal to leader nodes. + send_initial_votes(&overlays, committee_size, &nodes); + network.collect_messages(); + + let nodes = Arc::new(RwLock::new(nodes)); + for _ in 0..9 { + network.dispatch_after(&mut rng, Duration::from_millis(100)); + nodes.write().unwrap().par_iter_mut().for_each(|(_, node)| { + node.step(); + }); + network.collect_messages(); + } + + for (_, node) in nodes.read().unwrap().iter() { + assert_eq!(node.current_view(), 2); + } + } + + #[test] + fn get_related_nodes() { + // 0 + // 1 2 + // 3 4 5 6 + let test_cases = vec![ + ( + 0, + None, + Some(BTreeSet::from([1.into(), 2.into()])), + vec![DummyRole::Root], + ), + ( + 1, + Some(BTreeSet::from([0.into()])), + Some(BTreeSet::from([3.into(), 4.into()])), + vec![DummyRole::Internal], + ), + ( + 2, + Some(BTreeSet::from([0.into()])), + Some(BTreeSet::from([5.into(), 6.into()])), + vec![DummyRole::Internal], + ), + ( + 3, + Some(BTreeSet::from([1.into()])), + None, + vec![DummyRole::Leaf], + ), + ( + 4, + Some(BTreeSet::from([1.into()])), + None, + vec![DummyRole::Leaf], + ), + ( + 5, + Some(BTreeSet::from([2.into()])), + None, + vec![DummyRole::Leaf], + ), + ( + 6, + Some(BTreeSet::from([2.into()])), + None, + vec![DummyRole::Leader, DummyRole::Leaf], + ), + ]; + let mut rng = StepRng::new(1, 0); + let overlay = TreeOverlay::new(TreeSettings { + tree_type: Default::default(), + depth: 3, + committee_size: 1, + }); + let node_ids: Vec = overlay.nodes(); + let leaders = vec![6.into()]; + let layout = overlay.layout(&node_ids, &mut rng); + let view = ViewOverlay { leaders, layout }; + + for (node_id, expected_parents, expected_children, expected_roles) in test_cases { + let node_id = node_id.into(); + let parents = get_parent_nodes(node_id, &view); + let children = get_child_nodes(node_id, &view); + let role = get_roles(node_id, &view, &parents, &children); + assert_eq!(parents, expected_parents); + assert_eq!(children, expected_children); + assert_eq!(role, expected_roles); + } + } +} diff --git a/simulations/src/node/mod.rs b/simulations/src/node/mod.rs index 05e4ab99..95c68031 100644 --- a/simulations/src/node/mod.rs +++ b/simulations/src/node/mod.rs @@ -3,6 +3,7 @@ pub mod dummy; // std use std::{ + collections::BTreeMap, ops::{Deref, DerefMut}, sync::{Arc, RwLock}, time::Duration, @@ -116,13 +117,37 @@ impl core::iter::Sum for Duration { } } -/// A state that represents how nodes are interconnected in the network. -pub struct NetworkState { +#[derive(Clone, Debug)] +pub struct ViewOverlay { + pub leaders: Vec, pub layout: Layout, } pub type SharedState = Arc>; +/// A state that represents how nodes are interconnected in the network. +pub struct OverlayState { + pub all_nodes: Vec, + pub overlays: BTreeMap, +} + +pub trait OverlayGetter { + fn get_view(&self, index: usize) -> Option; + fn get_all_nodes(&self) -> Vec; +} + +impl OverlayGetter for SharedState { + fn get_view(&self, index: usize) -> Option { + let overlay_state = self.read().unwrap(); + overlay_state.overlays.get(&index).cloned() + } + + fn get_all_nodes(&self) -> Vec { + let overlay_state = self.read().unwrap(); + overlay_state.all_nodes.clone() + } +} + pub trait Node { type Settings; type State; diff --git a/simulations/src/overlay/mod.rs b/simulations/src/overlay/mod.rs index 7170a435..e8b0c60b 100644 --- a/simulations/src/overlay/mod.rs +++ b/simulations/src/overlay/mod.rs @@ -56,8 +56,8 @@ impl Layout { } } - pub fn committee(&self, node_id: NodeId) -> CommitteeId { - self.from_committee.get(&node_id).copied().unwrap() + pub fn committee(&self, node_id: NodeId) -> Option { + self.from_committee.get(&node_id).copied() } pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee { @@ -73,14 +73,14 @@ impl Layout { .map(|c| self.committees[&c].clone()) } - pub fn children(&self, committee_id: CommitteeId) -> &[CommitteeId] { - &self.children[&committee_id] + pub fn children(&self, committee_id: CommitteeId) -> Option<&Vec> { + self.children.get(&committee_id) } pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> { self.children(committee_id) .iter() - .map(|&committee_id| &self.committees[&committee_id]) + .flat_map(|&committees| committees.iter().map(|c| &self.committees[c])) .collect() } diff --git a/simulations/src/overlay/tree.rs b/simulations/src/overlay/tree.rs index e9ddbcdf..7dc03400 100644 --- a/simulations/src/overlay/tree.rs +++ b/simulations/src/overlay/tree.rs @@ -25,7 +25,7 @@ impl Default for TreeSettings { Self { tree_type: TreeType::default(), committee_size: 1, - depth: 1, + depth: 3, // Root, internal and leaf layers. } } } diff --git a/simulations/src/runner/sync_runner.rs b/simulations/src/runner/sync_runner.rs index f683fcb5..41a3963c 100644 --- a/simulations/src/runner/sync_runner.rs +++ b/simulations/src/runner/sync_runner.rs @@ -46,7 +46,7 @@ mod tests { }, node::{ dummy::{DummyMessage, DummyNetworkInterface, DummyNode, DummySettings}, - NetworkState, Node, NodeId, SharedState, + Node, NodeId, OverlayState, SharedState, ViewOverlay, }, overlay::{ tree::{TreeOverlay, TreeSettings}, @@ -58,7 +58,7 @@ mod tests { use crossbeam::channel; use rand::rngs::mock::StepRng; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, sync::{Arc, RwLock}, time::Duration, }; @@ -76,7 +76,7 @@ mod tests { fn init_dummy_nodes( node_ids: &[NodeId], network: &mut Network, - network_state: SharedState, + overlay_state: SharedState, ) -> Vec { node_ids .iter() @@ -88,7 +88,7 @@ mod tests { node_message_sender, network_message_receiver, ); - DummyNode::new(*node_id, network_state.clone(), network_interface) + DummyNode::new(*node_id, 0, overlay_state.clone(), network_interface) }) .collect() } @@ -105,18 +105,67 @@ mod tests { let node_ids: Vec = (0..settings.node_count).map(Into::into).collect(); let overlay = TreeOverlay::new(settings.overlay_settings.clone()); let mut network = init_network(&node_ids); - let network_state = Arc::new(RwLock::new(NetworkState { + let view = ViewOverlay { + leaders: overlay.leaders(&node_ids, 1, &mut rng).collect(), layout: overlay.layout(&node_ids, &mut rng), + }; + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: BTreeMap::from([(0, view.clone()), (1, view)]), })); - let nodes = init_dummy_nodes(&node_ids, &mut network, network_state); + let nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); let mut runner: SimulationRunner = SimulationRunner::new(network, nodes, settings); runner.step(); - let nodes = runner.nodes.read().unwrap(); + let nodes = runner.nodes.read().unwrap(); for node in nodes.iter() { - assert_eq!(node.current_view(), 1); + assert_eq!(node.current_view(), 0); } } + + #[test] + fn runner_send_receive() { + let settings: SimulationSettings = SimulationSettings { + node_count: 10, + committee_size: 1, + ..Default::default() + }; + + let mut rng = StepRng::new(1, 0); + let node_ids: Vec = (0..settings.node_count).map(Into::into).collect(); + let overlay = TreeOverlay::new(settings.overlay_settings.clone()); + let mut network = init_network(&node_ids); + let view = ViewOverlay { + leaders: overlay.leaders(&node_ids, 1, &mut rng).collect(), + layout: overlay.layout(&node_ids, &mut rng), + }; + let overlay_state = Arc::new(RwLock::new(OverlayState { + all_nodes: node_ids.clone(), + overlays: BTreeMap::from([ + (0, view.clone()), + (1, view.clone()), + (42, view.clone()), + (43, view), + ]), + })); + let nodes = init_dummy_nodes(&node_ids, &mut network, overlay_state); + + for node in nodes.iter() { + // All nodes send one message to NodeId(1). + // Nodes can send messages to themselves. + node.send_message(node_ids[1], DummyMessage::Proposal(42.into())); + } + network.collect_messages(); + + let mut runner: SimulationRunner = + SimulationRunner::new(network, nodes, settings); + + runner.step(); + + let nodes = runner.nodes.read().unwrap(); + let state = nodes[1].state(); + assert_eq!(state.message_count, 10); + } }