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:
parent
a19278480d
commit
a87418c908
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue