Test voting steps with various node roles (#114)

* Single step send receive runner tests

* Rename network to overlay state

* Store every overlay that might happen

* Get related nodes for from node id

* Add leader role

* WIP dummy handle events

* Leader and root roles

* Add intent as helper to dummy vote

* Check if has enough votes

* Proposal to vote to new view tests

* Update sync runner tests

* Smallrng in send receive tests

* Send initial vote next view leaders in tests

* Local view struct for node

* Large node number tests

* Fix internal to internal case
This commit is contained in:
gusto 2023-04-10 13:18:20 +03:00 committed by GitHub
parent a19278480d
commit a87418c908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 925 additions and 41 deletions

View File

@ -80,12 +80,12 @@ where
/// delay has passed. /// delay has passed.
pub fn dispatch_after<R: Rng>(&mut self, rng: &mut R, time_passed: Duration) { pub fn dispatch_after<R: Rng>(&mut self, rng: &mut R, time_passed: Duration) {
self.network_time += time_passed; self.network_time += time_passed;
let mut delayed = vec![]; let mut delayed = vec![];
while let Some((network_time, message)) = self.messages.pop() { while let Some((network_time, message)) = self.messages.pop() {
// TODO: Handle message drops (remove unwrap). // If cost is None, message won't get sent nor it will be
let delay = self // readded to the pending messages list.
.send_message_cost(rng, message.from, message.to) if let Some(delay) = self.send_message_cost(rng, message.from, message.to) {
.unwrap();
if network_time.add(delay) <= self.network_time { if network_time.add(delay) <= self.network_time {
let to_node = self.to_node_senders.get(&message.to).unwrap(); let to_node = self.to_node_senders.get(&message.to).unwrap();
to_node.send(message).expect("Node should have connection"); to_node.send(message).expect("Node should have connection");
@ -93,6 +93,7 @@ where
delayed.push((network_time, message)); delayed.push((network_time, message));
} }
} }
}
self.messages = delayed; self.messages = delayed;
} }
} }

View File

@ -1,4 +1,5 @@
// std // std
use std::collections::{BTreeMap, BTreeSet};
// crates // crates
use crossbeam::channel::{Receiver, Sender}; use crossbeam::channel::{Receiver, Sender};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -8,42 +9,343 @@ use crate::{
node::{Node, NodeId}, node::{Node, NodeId},
}; };
use super::{NetworkState, SharedState}; use super::{OverlayGetter, OverlayState, SharedState, ViewOverlay};
#[derive(Debug, Default, Serialize)] #[derive(Debug, Default, Serialize)]
pub struct DummyState { pub struct DummyState {
pub current_view: usize, pub current_view: usize,
pub message_count: usize,
pub view_state: BTreeMap<usize, DummyViewState>,
}
#[derive(Debug, Default, Serialize)]
pub struct DummyViewState {
proposal_received: bool,
vote_received_count: usize,
vote_sent: bool,
} }
#[derive(Clone, Default, Deserialize)] #[derive(Clone, Default, Deserialize)]
pub struct DummySettings {} 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<usize> 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<usize> for Block {
fn from(id: usize) -> Self {
Self { view: id }
}
}
#[derive(Debug, Clone)]
pub enum DummyMessage { pub enum DummyMessage {
EventOne(usize), Vote(Vote),
EventTwo(usize), Proposal(Block),
}
struct LocalView {
pub next_view_leaders: Vec<NodeId>,
pub current_roots: Option<BTreeSet<NodeId>>,
pub children: Option<BTreeSet<NodeId>>,
pub parents: Option<BTreeSet<NodeId>>,
pub roles: Vec<DummyRole>,
}
impl LocalView {
pub fn new<O: OverlayGetter>(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 { pub struct DummyNode {
node_id: NodeId, node_id: NodeId,
state: DummyState, state: DummyState,
_settings: DummySettings, _settings: DummySettings,
_network_state: SharedState<NetworkState>, overlay_state: SharedState<OverlayState>,
network_interface: DummyNetworkInterface, 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 { impl DummyNode {
pub fn new( pub fn new(
node_id: NodeId, node_id: NodeId,
_network_state: SharedState<NetworkState>, view_id: usize,
overlay_state: SharedState<OverlayState>,
network_interface: DummyNetworkInterface, network_interface: DummyNetworkInterface,
) -> Self { ) -> Self {
Self { Self {
node_id, node_id,
state: DummyState { current_view: 0 }, state: Default::default(),
_settings: DummySettings {}, _settings: Default::default(),
_network_state, overlay_state: overlay_state.clone(),
network_interface, 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<BTreeSet<NodeId>>) -> 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<DummyMessage>) {
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<DummyMessage>) {
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<DummyMessage>) {
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<DummyMessage>) {
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<DummyMessage>) {
// 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) { fn step(&mut self) {
let incoming_messages = self.network_interface.receive_messages(); 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 { incoming_messages
match message.payload { .iter()
DummyMessage::EventOne(_) => todo!(), .for_each(|m| self.handle_message(m));
DummyMessage::EventTwo(_) => todo!(),
}
}
} }
} }
@ -109,3 +408,513 @@ impl NetworkInterface for DummyNetworkInterface {
self.receiver.try_iter().collect() self.receiver.try_iter().collect()
} }
} }
fn get_parent_nodes(node_id: NodeId, view: &ViewOverlay) -> Option<BTreeSet<NodeId>> {
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<BTreeSet<NodeId>> {
let committee_id = view.layout.committee(node_id)?;
let child_nodes: BTreeSet<NodeId> = 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<BTreeSet<NodeId>>,
children: &Option<BTreeSet<NodeId>>,
) -> Vec<DummyRole> {
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<DummyMessage> {
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<DummyMessage>,
overlay_state: SharedState<OverlayState>,
) -> HashMap<NodeId, DummyNode> {
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<O: Overlay, R: Rng>(
node_ids: &[NodeId],
overlay: O,
overlay_count: usize,
leader_count: usize,
rng: &mut R,
) -> BTreeMap<usize, ViewOverlay> {
(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<usize, ViewOverlay>,
committee_size: usize,
nodes: &HashMap<NodeId, DummyNode>,
) {
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<NodeId> = 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<NodeId> = (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<NodeId> = (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<NodeId> = (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<NodeId> = 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);
}
}
}

View File

@ -3,6 +3,7 @@ pub mod dummy;
// std // std
use std::{ use std::{
collections::BTreeMap,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::{Arc, RwLock}, sync::{Arc, RwLock},
time::Duration, time::Duration,
@ -116,13 +117,37 @@ impl core::iter::Sum<StepTime> for Duration {
} }
} }
/// A state that represents how nodes are interconnected in the network. #[derive(Clone, Debug)]
pub struct NetworkState { pub struct ViewOverlay {
pub leaders: Vec<NodeId>,
pub layout: Layout, pub layout: Layout,
} }
pub type SharedState<S> = Arc<RwLock<S>>; pub type SharedState<S> = Arc<RwLock<S>>;
/// A state that represents how nodes are interconnected in the network.
pub struct OverlayState {
pub all_nodes: Vec<NodeId>,
pub overlays: BTreeMap<usize, ViewOverlay>,
}
pub trait OverlayGetter {
fn get_view(&self, index: usize) -> Option<ViewOverlay>;
fn get_all_nodes(&self) -> Vec<NodeId>;
}
impl OverlayGetter for SharedState<OverlayState> {
fn get_view(&self, index: usize) -> Option<ViewOverlay> {
let overlay_state = self.read().unwrap();
overlay_state.overlays.get(&index).cloned()
}
fn get_all_nodes(&self) -> Vec<NodeId> {
let overlay_state = self.read().unwrap();
overlay_state.all_nodes.clone()
}
}
pub trait Node { pub trait Node {
type Settings; type Settings;
type State; type State;

View File

@ -56,8 +56,8 @@ impl Layout {
} }
} }
pub fn committee(&self, node_id: NodeId) -> CommitteeId { pub fn committee(&self, node_id: NodeId) -> Option<CommitteeId> {
self.from_committee.get(&node_id).copied().unwrap() self.from_committee.get(&node_id).copied()
} }
pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee { pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee {
@ -73,14 +73,14 @@ impl Layout {
.map(|c| self.committees[&c].clone()) .map(|c| self.committees[&c].clone())
} }
pub fn children(&self, committee_id: CommitteeId) -> &[CommitteeId] { pub fn children(&self, committee_id: CommitteeId) -> Option<&Vec<CommitteeId>> {
&self.children[&committee_id] self.children.get(&committee_id)
} }
pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> { pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> {
self.children(committee_id) self.children(committee_id)
.iter() .iter()
.map(|&committee_id| &self.committees[&committee_id]) .flat_map(|&committees| committees.iter().map(|c| &self.committees[c]))
.collect() .collect()
} }

View File

@ -25,7 +25,7 @@ impl Default for TreeSettings {
Self { Self {
tree_type: TreeType::default(), tree_type: TreeType::default(),
committee_size: 1, committee_size: 1,
depth: 1, depth: 3, // Root, internal and leaf layers.
} }
} }
} }

View File

@ -46,7 +46,7 @@ mod tests {
}, },
node::{ node::{
dummy::{DummyMessage, DummyNetworkInterface, DummyNode, DummySettings}, dummy::{DummyMessage, DummyNetworkInterface, DummyNode, DummySettings},
NetworkState, Node, NodeId, SharedState, Node, NodeId, OverlayState, SharedState, ViewOverlay,
}, },
overlay::{ overlay::{
tree::{TreeOverlay, TreeSettings}, tree::{TreeOverlay, TreeSettings},
@ -58,7 +58,7 @@ mod tests {
use crossbeam::channel; use crossbeam::channel;
use rand::rngs::mock::StepRng; use rand::rngs::mock::StepRng;
use std::{ use std::{
collections::HashMap, collections::{BTreeMap, HashMap},
sync::{Arc, RwLock}, sync::{Arc, RwLock},
time::Duration, time::Duration,
}; };
@ -76,7 +76,7 @@ mod tests {
fn init_dummy_nodes( fn init_dummy_nodes(
node_ids: &[NodeId], node_ids: &[NodeId],
network: &mut Network<DummyMessage>, network: &mut Network<DummyMessage>,
network_state: SharedState<NetworkState>, overlay_state: SharedState<OverlayState>,
) -> Vec<DummyNode> { ) -> Vec<DummyNode> {
node_ids node_ids
.iter() .iter()
@ -88,7 +88,7 @@ mod tests {
node_message_sender, node_message_sender,
network_message_receiver, network_message_receiver,
); );
DummyNode::new(*node_id, network_state.clone(), network_interface) DummyNode::new(*node_id, 0, overlay_state.clone(), network_interface)
}) })
.collect() .collect()
} }
@ -105,18 +105,67 @@ mod tests {
let node_ids: Vec<NodeId> = (0..settings.node_count).map(Into::into).collect(); let node_ids: Vec<NodeId> = (0..settings.node_count).map(Into::into).collect();
let overlay = TreeOverlay::new(settings.overlay_settings.clone()); let overlay = TreeOverlay::new(settings.overlay_settings.clone());
let mut network = init_network(&node_ids); 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), 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<DummyMessage, DummyNode, TreeOverlay> = let mut runner: SimulationRunner<DummyMessage, DummyNode, TreeOverlay> =
SimulationRunner::new(network, nodes, settings); SimulationRunner::new(network, nodes, settings);
runner.step(); runner.step();
let nodes = runner.nodes.read().unwrap(); let nodes = runner.nodes.read().unwrap();
for node in nodes.iter() {
assert_eq!(node.current_view(), 0);
}
}
#[test]
fn runner_send_receive() {
let settings: SimulationSettings<DummySettings, TreeSettings> = SimulationSettings {
node_count: 10,
committee_size: 1,
..Default::default()
};
let mut rng = StepRng::new(1, 0);
let node_ids: Vec<NodeId> = (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() { for node in nodes.iter() {
assert_eq!(node.current_view(), 1); // 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<DummyMessage, DummyNode, TreeOverlay> =
SimulationRunner::new(network, nodes, settings);
runner.step();
let nodes = runner.nodes.read().unwrap();
let state = nodes[1].state();
assert_eq!(state.message_count, 10);
} }
} }