Simulations overlay topo info (#479)
* Missing json feature for building standalone simapp * Move dummy sim overlay to tests module * OverlayInfoExt in simulation app * Dump overlay info in simapp
This commit is contained in:
parent
1553f29bd9
commit
54dd96dfff
@ -11,7 +11,9 @@ path = "src/bin/app/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
blake2 = "0.10"
|
||||||
bls-signatures = "0.14"
|
bls-signatures = "0.14"
|
||||||
|
digest = "0.10"
|
||||||
csv = "1"
|
csv = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
ctrlc = "3.4"
|
ctrlc = "3.4"
|
||||||
@ -36,7 +38,7 @@ serde_with = "2.3"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
tracing = { version = "0.1", default-features = false, features = ["log", "attributes"] }
|
tracing = { version = "0.1", default-features = false, features = ["log", "attributes"] }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "tracing-log"]}
|
tracing-subscriber = { version = "0.3", features = ["json", "env-filter", "tracing-log"]}
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2", features = ["js"] }
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
|
@ -44,6 +44,8 @@ pub struct SimulationApp {
|
|||||||
log_format: log::LogFormat,
|
log_format: log::LogFormat,
|
||||||
#[clap(long, default_value = "stdout")]
|
#[clap(long, default_value = "stdout")]
|
||||||
log_to: log::LogOutput,
|
log_to: log::LogOutput,
|
||||||
|
#[clap(long)]
|
||||||
|
dump_overlay_info: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimulationApp {
|
impl SimulationApp {
|
||||||
@ -53,6 +55,7 @@ impl SimulationApp {
|
|||||||
stream_type,
|
stream_type,
|
||||||
log_format: _,
|
log_format: _,
|
||||||
log_to: _,
|
log_to: _,
|
||||||
|
dump_overlay_info,
|
||||||
} = self;
|
} = self;
|
||||||
let simulation_settings: SimulationSettings = load_json_from_file(&input_settings)?;
|
let simulation_settings: SimulationSettings = load_json_from_file(&input_settings)?;
|
||||||
|
|
||||||
@ -74,6 +77,18 @@ impl SimulationApp {
|
|||||||
|
|
||||||
let ids = node_ids.clone();
|
let ids = node_ids.clone();
|
||||||
let network = Arc::new(Mutex::new(Network::new(regions_data, seed)));
|
let network = Arc::new(Mutex::new(Network::new(regions_data, seed)));
|
||||||
|
|
||||||
|
if dump_overlay_info {
|
||||||
|
dump_json_to_file(
|
||||||
|
Path::new("overlay_info.json"),
|
||||||
|
&overlay_node::overlay_info(
|
||||||
|
node_ids.clone(),
|
||||||
|
node_ids.first().copied().unwrap(),
|
||||||
|
&simulation_settings.overlay_settings,
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let nodes: Vec<BoxedNode<CarnotSettings, CarnotState>> = node_ids
|
let nodes: Vec<BoxedNode<CarnotSettings, CarnotState>> = node_ids
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.copied()
|
.copied()
|
||||||
@ -206,6 +221,11 @@ fn load_json_from_file<T: DeserializeOwned>(path: &Path) -> anyhow::Result<T> {
|
|||||||
Ok(serde_json::from_reader(f)?)
|
Ok(serde_json::from_reader(f)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dump_json_to_file<T: Serialize>(path: &Path, data: &T) -> anyhow::Result<()> {
|
||||||
|
let f = File::create(path).map_err(Box::new)?;
|
||||||
|
Ok(serde_json::to_writer(f, data)?)
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let app: SimulationApp = SimulationApp::parse();
|
let app: SimulationApp = SimulationApp::parse();
|
||||||
log::config_tracing(app.log_format, &app.log_to);
|
log::config_tracing(app.log_format, &app.log_to);
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use consensus_engine::overlay::{BranchOverlay, RandomBeaconState};
|
use consensus_engine::overlay::{BranchOverlay, FisherYatesShuffle, RandomBeaconState};
|
||||||
|
use consensus_engine::Overlay;
|
||||||
use consensus_engine::{
|
use consensus_engine::{
|
||||||
overlay::{FlatOverlay, FreezeMembership, RoundRobin, TreeOverlay},
|
overlay::{FlatOverlay, FreezeMembership, RoundRobin, TreeOverlay},
|
||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use simulations::overlay::overlay_info::{OverlayInfo, OverlayInfoExt};
|
||||||
|
use simulations::settings::OverlaySettings;
|
||||||
use simulations::{
|
use simulations::{
|
||||||
network::InMemoryNetworkInterface,
|
network::InMemoryNetworkInterface,
|
||||||
node::carnot::{messages::CarnotMessage, CarnotNode, CarnotSettings, CarnotState},
|
node::carnot::{messages::CarnotMessage, CarnotNode, CarnotSettings, CarnotState},
|
||||||
@ -11,6 +14,46 @@ use simulations::{
|
|||||||
settings::SimulationSettings,
|
settings::SimulationSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn overlay_info(
|
||||||
|
nodes: Vec<NodeId>,
|
||||||
|
leader: NodeId,
|
||||||
|
overlay_settings: &OverlaySettings,
|
||||||
|
) -> OverlayInfo {
|
||||||
|
match &overlay_settings {
|
||||||
|
simulations::settings::OverlaySettings::Flat => {
|
||||||
|
FlatOverlay::<RoundRobin, FisherYatesShuffle>::new(
|
||||||
|
consensus_engine::overlay::FlatOverlaySettings {
|
||||||
|
nodes: nodes.to_vec(),
|
||||||
|
leader: RoundRobin::new(),
|
||||||
|
leader_super_majority_threshold: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.info()
|
||||||
|
}
|
||||||
|
simulations::settings::OverlaySettings::Tree(tree_settings) => {
|
||||||
|
TreeOverlay::new(consensus_engine::overlay::TreeOverlaySettings {
|
||||||
|
nodes,
|
||||||
|
current_leader: leader,
|
||||||
|
number_of_committees: tree_settings.number_of_committees,
|
||||||
|
leader: RoundRobin::new(),
|
||||||
|
committee_membership: RandomBeaconState::initial_sad_from_entropy([0; 32]),
|
||||||
|
super_majority_threshold: None,
|
||||||
|
})
|
||||||
|
.info()
|
||||||
|
}
|
||||||
|
simulations::settings::OverlaySettings::Branch(branch_settings) => {
|
||||||
|
BranchOverlay::new(consensus_engine::overlay::BranchOverlaySettings {
|
||||||
|
nodes,
|
||||||
|
current_leader: leader,
|
||||||
|
branch_depth: branch_settings.branch_depth,
|
||||||
|
leader: RoundRobin::new(),
|
||||||
|
committee_membership: RandomBeaconState::initial_sad_from_entropy([0; 32]),
|
||||||
|
})
|
||||||
|
.info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_overlay_node<R: Rng>(
|
pub fn to_overlay_node<R: Rng>(
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
nodes: Vec<NodeId>,
|
nodes: Vec<NodeId>,
|
||||||
|
@ -451,7 +451,7 @@ mod tests {
|
|||||||
dummy::{get_child_nodes, get_parent_nodes, get_roles, DummyRole},
|
dummy::{get_child_nodes, get_parent_nodes, get_roles, DummyRole},
|
||||||
Node, NodeId, NodeIdExt, OverlayState, SharedState, SimulationOverlay, ViewOverlay,
|
Node, NodeId, NodeIdExt, OverlayState, SharedState, SimulationOverlay, ViewOverlay,
|
||||||
},
|
},
|
||||||
overlay::{
|
overlay::tests::{
|
||||||
tree::{TreeOverlay, TreeSettings},
|
tree::{TreeOverlay, TreeSettings},
|
||||||
Overlay,
|
Overlay,
|
||||||
},
|
},
|
||||||
|
@ -16,7 +16,7 @@ use std::{
|
|||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// internal
|
// internal
|
||||||
use crate::overlay::{Layout, OverlaySettings, SimulationOverlay};
|
use crate::overlay::tests::{Layout, OverlaySettings, SimulationOverlay};
|
||||||
|
|
||||||
pub use consensus_engine::NodeId;
|
pub use consensus_engine::NodeId;
|
||||||
|
|
||||||
|
@ -1,178 +1,2 @@
|
|||||||
pub mod flat;
|
pub mod overlay_info;
|
||||||
pub mod tree;
|
pub mod tests;
|
||||||
|
|
||||||
// std
|
|
||||||
use std::collections::{BTreeSet, HashMap};
|
|
||||||
// crates
|
|
||||||
use rand::Rng;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
// internal
|
|
||||||
use crate::node::{CommitteeId, NodeId};
|
|
||||||
|
|
||||||
use self::tree::TreeSettings;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct Committee {
|
|
||||||
pub nodes: BTreeSet<NodeId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Committee {
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.nodes.len() == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Leaders = BTreeSet<NodeId>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct Layout {
|
|
||||||
pub committees: HashMap<CommitteeId, Committee>,
|
|
||||||
pub from_committee: HashMap<NodeId, CommitteeId>,
|
|
||||||
pub parent: HashMap<CommitteeId, CommitteeId>,
|
|
||||||
pub children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
|
||||||
pub layers: HashMap<CommitteeId, Vec<CommitteeId>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Layout {
|
|
||||||
pub fn new(
|
|
||||||
committees: HashMap<CommitteeId, Committee>,
|
|
||||||
parent: HashMap<CommitteeId, CommitteeId>,
|
|
||||||
children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
|
||||||
layers: HashMap<CommitteeId, Vec<CommitteeId>>,
|
|
||||||
) -> Self {
|
|
||||||
let from_committee = committees
|
|
||||||
.iter()
|
|
||||||
.flat_map(|(&committee_id, committee)| {
|
|
||||||
committee
|
|
||||||
.nodes
|
|
||||||
.iter()
|
|
||||||
.map(move |&node_id| (node_id, committee_id))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self {
|
|
||||||
committees,
|
|
||||||
from_committee,
|
|
||||||
parent,
|
|
||||||
children,
|
|
||||||
layers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn committee(&self, node_id: NodeId) -> Option<CommitteeId> {
|
|
||||||
self.from_committee.get(&node_id).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee {
|
|
||||||
&self.committees[&committee_id]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parent(&self, committee_id: CommitteeId) -> Option<CommitteeId> {
|
|
||||||
self.parent.get(&committee_id).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parent_nodes(&self, committee_id: CommitteeId) -> Option<Committee> {
|
|
||||||
self.parent(committee_id)
|
|
||||||
.map(|c| self.committees[&c].clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn children(&self, committee_id: CommitteeId) -> Option<&Vec<CommitteeId>> {
|
|
||||||
self.children.get(&committee_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> {
|
|
||||||
self.children(committee_id)
|
|
||||||
.iter()
|
|
||||||
.flat_map(|&committees| committees.iter().map(|c| &self.committees[c]))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
|
|
||||||
self.from_committee.keys().copied()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum SimulationOverlay {
|
|
||||||
Flat(flat::FlatOverlay),
|
|
||||||
Tree(tree::TreeOverlay),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum OverlaySettings {
|
|
||||||
Flat,
|
|
||||||
Tree(TreeSettings),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OverlaySettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Tree(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TreeSettings> for OverlaySettings {
|
|
||||||
fn from(settings: TreeSettings) -> OverlaySettings {
|
|
||||||
OverlaySettings::Tree(settings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryInto<TreeSettings> for OverlaySettings {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn try_into(self) -> Result<TreeSettings, Self::Error> {
|
|
||||||
if let Self::Tree(settings) = self {
|
|
||||||
Ok(settings)
|
|
||||||
} else {
|
|
||||||
Err("unable to convert to tree settings".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Overlay for SimulationOverlay {
|
|
||||||
fn nodes(&self) -> Vec<NodeId> {
|
|
||||||
match self {
|
|
||||||
SimulationOverlay::Flat(overlay) => overlay.nodes(),
|
|
||||||
SimulationOverlay::Tree(overlay) => overlay.nodes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leaders<R: Rng>(
|
|
||||||
&self,
|
|
||||||
nodes: &[NodeId],
|
|
||||||
size: usize,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Box<dyn Iterator<Item = NodeId>> {
|
|
||||||
match self {
|
|
||||||
SimulationOverlay::Flat(overlay) => overlay.leaders(nodes, size, rng),
|
|
||||||
SimulationOverlay::Tree(overlay) => overlay.leaders(nodes, size, rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout<R: Rng>(&self, nodes: &[NodeId], rng: &mut R) -> Layout {
|
|
||||||
match self {
|
|
||||||
SimulationOverlay::Flat(overlay) => overlay.layout(nodes, rng),
|
|
||||||
SimulationOverlay::Tree(overlay) => overlay.layout(nodes, rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Overlay {
|
|
||||||
fn nodes(&self) -> Vec<NodeId>;
|
|
||||||
fn leaders<R: Rng>(
|
|
||||||
&self,
|
|
||||||
nodes: &[NodeId],
|
|
||||||
size: usize,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Box<dyn Iterator<Item = NodeId>>;
|
|
||||||
fn layout<R: Rng>(&self, nodes: &[NodeId], rng: &mut R) -> Layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes a reference to the simulation_settings and returns a SimulationOverlay instance based
|
|
||||||
// on the overlay settings specified in simulation_settings.
|
|
||||||
pub fn create_overlay(overlay_settings: &OverlaySettings) -> SimulationOverlay {
|
|
||||||
match &overlay_settings {
|
|
||||||
OverlaySettings::Flat => SimulationOverlay::Flat(flat::FlatOverlay::new()),
|
|
||||||
OverlaySettings::Tree(settings) => {
|
|
||||||
SimulationOverlay::Tree(tree::TreeOverlay::new(settings.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
181
simulations/src/overlay/overlay_info.rs
Normal file
181
simulations/src/overlay/overlay_info.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
use consensus_engine::{CommitteeId, NodeId, Overlay};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::{BTreeSet, HashMap, VecDeque};
|
||||||
|
|
||||||
|
pub type Blake2bU32 = blake2::Blake2b<digest::typenum::U32>;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct OverlayInfo {
|
||||||
|
pub committees: BTreeSet<CommitteeId>,
|
||||||
|
pub committee_sizes: HashMap<CommitteeId, usize>,
|
||||||
|
pub edges: Vec<(CommitteeId, CommitteeId)>,
|
||||||
|
pub next_leader: NodeId,
|
||||||
|
pub root_id: CommitteeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OverlayInfoExt {
|
||||||
|
fn info(&self) -> OverlayInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Overlay> OverlayInfoExt for T {
|
||||||
|
fn info(&self) -> OverlayInfo {
|
||||||
|
let mut committees = BTreeSet::new();
|
||||||
|
let mut edges = Vec::new();
|
||||||
|
let mut committee_sizes = HashMap::new();
|
||||||
|
|
||||||
|
let next_leader = self.next_leader();
|
||||||
|
let root = self.root_committee();
|
||||||
|
let root_id = root.id::<Blake2bU32>();
|
||||||
|
committees.insert(root_id);
|
||||||
|
committee_sizes.insert(root_id, root.len());
|
||||||
|
|
||||||
|
let mut queue = VecDeque::new();
|
||||||
|
queue.push_back(root);
|
||||||
|
|
||||||
|
while let Some(current_committee) = queue.pop_front() {
|
||||||
|
let current_id = current_committee.id::<Blake2bU32>();
|
||||||
|
|
||||||
|
if let Some(committee_node) = current_committee.iter().next() {
|
||||||
|
for child in self.child_committees(*committee_node) {
|
||||||
|
let child_id = child.id::<Blake2bU32>();
|
||||||
|
committees.insert(child_id);
|
||||||
|
committee_sizes.insert(child_id, child.len());
|
||||||
|
edges.push((current_id, child_id));
|
||||||
|
queue.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OverlayInfo {
|
||||||
|
committees,
|
||||||
|
committee_sizes,
|
||||||
|
edges,
|
||||||
|
next_leader,
|
||||||
|
root_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use consensus_engine::{
|
||||||
|
overlay::{
|
||||||
|
BranchOverlay, BranchOverlaySettings, FisherYatesShuffle, RoundRobin, TreeOverlay,
|
||||||
|
TreeOverlaySettings,
|
||||||
|
},
|
||||||
|
NodeId, Overlay,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
const ENTROPY: [u8; 32] = [0; 32];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tree_overlay_info() {
|
||||||
|
let nodes: Vec<_> = (0..7).map(|i| NodeId::new([i as u8; 32])).collect();
|
||||||
|
let overlay = TreeOverlay::new(TreeOverlaySettings {
|
||||||
|
nodes: nodes.clone(),
|
||||||
|
current_leader: nodes[0],
|
||||||
|
number_of_committees: 3,
|
||||||
|
leader: RoundRobin::new(),
|
||||||
|
committee_membership: FisherYatesShuffle::new(ENTROPY),
|
||||||
|
super_majority_threshold: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let root_committee = overlay.root_committee();
|
||||||
|
let root_node = root_committee.iter().next().unwrap();
|
||||||
|
let child_committees = overlay.child_committees(*root_node);
|
||||||
|
let child1 = child_committees[0].clone();
|
||||||
|
let child2 = child_committees[1].clone();
|
||||||
|
|
||||||
|
let info = overlay.info();
|
||||||
|
let info_children: Vec<&(CommitteeId, CommitteeId)> = info
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.filter(|(p, _)| *p == info.root_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(info.committees.len(), 3);
|
||||||
|
assert_eq!(root_committee.id::<Blake2bU32>(), info.root_id);
|
||||||
|
|
||||||
|
let mut info_child_iter = info_children.iter();
|
||||||
|
let info_child1 = info_child_iter.next().map(|(_, c)| c).unwrap();
|
||||||
|
let info_child2 = info_child_iter.next().map(|(_, c)| c).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(child1.id::<Blake2bU32>(), *info_child1);
|
||||||
|
assert_eq!(child2.id::<Blake2bU32>(), *info_child2);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
child1.len(),
|
||||||
|
*info.committee_sizes.get(info_child1).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
child2.len(),
|
||||||
|
*info.committee_sizes.get(info_child2).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_overlay_info() {
|
||||||
|
let nodes: Vec<_> = (0..7).map(|i| NodeId::new([i as u8; 32])).collect();
|
||||||
|
let overlay = BranchOverlay::new(BranchOverlaySettings {
|
||||||
|
nodes: nodes.clone(),
|
||||||
|
current_leader: nodes[0],
|
||||||
|
branch_depth: 3,
|
||||||
|
leader: RoundRobin::new(),
|
||||||
|
committee_membership: FisherYatesShuffle::new(ENTROPY),
|
||||||
|
});
|
||||||
|
|
||||||
|
let root_committee = overlay.root_committee();
|
||||||
|
let root_node = root_committee.iter().next().unwrap();
|
||||||
|
|
||||||
|
let info = overlay.info();
|
||||||
|
assert_eq!(info.committees.len(), 3);
|
||||||
|
assert_eq!(root_committee.id::<Blake2bU32>(), info.root_id);
|
||||||
|
|
||||||
|
assert_eq!(overlay.child_committees(*root_node).len(), 1);
|
||||||
|
let layer1 = overlay
|
||||||
|
.child_committees(*root_node)
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let layer1_node = layer1.iter().next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(overlay.child_committees(*layer1_node).len(), 1);
|
||||||
|
let info_layer1: Vec<&(CommitteeId, CommitteeId)> = info
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.filter(|(p, _)| *p == info.root_id)
|
||||||
|
.collect();
|
||||||
|
assert_eq!(info_layer1.len(), 1);
|
||||||
|
let info_layer1 = info_layer1.first().map(|(_, c)| c).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(layer1.id::<Blake2bU32>(), *info_layer1);
|
||||||
|
assert_eq!(
|
||||||
|
layer1.len(),
|
||||||
|
*info.committee_sizes.get(info_layer1).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let layer2 = overlay
|
||||||
|
.child_committees(*layer1_node)
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let layer2_node = layer2.iter().next().unwrap();
|
||||||
|
assert_eq!(overlay.child_committees(*layer2_node).len(), 0);
|
||||||
|
|
||||||
|
let info_layer2: Vec<&(CommitteeId, CommitteeId)> = info
|
||||||
|
.edges
|
||||||
|
.iter()
|
||||||
|
.filter(|(p, _)| *p == layer1.id::<Blake2bU32>())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(info_layer2.len(), 1);
|
||||||
|
let info_layer2 = info_layer2.first().map(|(_, c)| c).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(layer2.id::<Blake2bU32>(), *info_layer2);
|
||||||
|
assert_eq!(
|
||||||
|
layer2.len(),
|
||||||
|
*info.committee_sizes.get(info_layer2).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,8 @@ use consensus_engine::NodeId;
|
|||||||
use rand::prelude::IteratorRandom;
|
use rand::prelude::IteratorRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
// internal
|
// internal
|
||||||
use super::Overlay;
|
use super::{Committee, Layout, Overlay};
|
||||||
use crate::node::NodeIdExt;
|
use crate::node::NodeIdExt;
|
||||||
use crate::overlay::{Committee, Layout};
|
|
||||||
|
|
||||||
pub struct FlatOverlay;
|
pub struct FlatOverlay;
|
||||||
impl FlatOverlay {
|
impl FlatOverlay {
|
178
simulations/src/overlay/tests/mod.rs
Normal file
178
simulations/src/overlay/tests/mod.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
pub mod flat;
|
||||||
|
pub mod tree;
|
||||||
|
|
||||||
|
// std
|
||||||
|
use std::collections::{BTreeSet, HashMap};
|
||||||
|
// crates
|
||||||
|
use rand::Rng;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
// internal
|
||||||
|
use crate::node::{CommitteeId, NodeId};
|
||||||
|
|
||||||
|
use self::tree::TreeSettings;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Committee {
|
||||||
|
pub nodes: BTreeSet<NodeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Committee {
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.nodes.len() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Leaders = BTreeSet<NodeId>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Layout {
|
||||||
|
pub committees: HashMap<CommitteeId, Committee>,
|
||||||
|
pub from_committee: HashMap<NodeId, CommitteeId>,
|
||||||
|
pub parent: HashMap<CommitteeId, CommitteeId>,
|
||||||
|
pub children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||||
|
pub layers: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn new(
|
||||||
|
committees: HashMap<CommitteeId, Committee>,
|
||||||
|
parent: HashMap<CommitteeId, CommitteeId>,
|
||||||
|
children: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||||
|
layers: HashMap<CommitteeId, Vec<CommitteeId>>,
|
||||||
|
) -> Self {
|
||||||
|
let from_committee = committees
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(&committee_id, committee)| {
|
||||||
|
committee
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.map(move |&node_id| (node_id, committee_id))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
committees,
|
||||||
|
from_committee,
|
||||||
|
parent,
|
||||||
|
children,
|
||||||
|
layers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn committee(&self, node_id: NodeId) -> Option<CommitteeId> {
|
||||||
|
self.from_committee.get(&node_id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn committee_nodes(&self, committee_id: CommitteeId) -> &Committee {
|
||||||
|
&self.committees[&committee_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self, committee_id: CommitteeId) -> Option<CommitteeId> {
|
||||||
|
self.parent.get(&committee_id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent_nodes(&self, committee_id: CommitteeId) -> Option<Committee> {
|
||||||
|
self.parent(committee_id)
|
||||||
|
.map(|c| self.committees[&c].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self, committee_id: CommitteeId) -> Option<&Vec<CommitteeId>> {
|
||||||
|
self.children.get(&committee_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children_nodes(&self, committee_id: CommitteeId) -> Vec<&Committee> {
|
||||||
|
self.children(committee_id)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&committees| committees.iter().map(|c| &self.committees[c]))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
|
||||||
|
self.from_committee.keys().copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SimulationOverlay {
|
||||||
|
Flat(flat::FlatOverlay),
|
||||||
|
Tree(tree::TreeOverlay),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum OverlaySettings {
|
||||||
|
Flat,
|
||||||
|
Tree(TreeSettings),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OverlaySettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Tree(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TreeSettings> for OverlaySettings {
|
||||||
|
fn from(settings: TreeSettings) -> OverlaySettings {
|
||||||
|
OverlaySettings::Tree(settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<TreeSettings> for OverlaySettings {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<TreeSettings, Self::Error> {
|
||||||
|
if let Self::Tree(settings) = self {
|
||||||
|
Ok(settings)
|
||||||
|
} else {
|
||||||
|
Err("unable to convert to tree settings".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Overlay for SimulationOverlay {
|
||||||
|
fn nodes(&self) -> Vec<NodeId> {
|
||||||
|
match self {
|
||||||
|
SimulationOverlay::Flat(overlay) => overlay.nodes(),
|
||||||
|
SimulationOverlay::Tree(overlay) => overlay.nodes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leaders<R: Rng>(
|
||||||
|
&self,
|
||||||
|
nodes: &[NodeId],
|
||||||
|
size: usize,
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Box<dyn Iterator<Item = NodeId>> {
|
||||||
|
match self {
|
||||||
|
SimulationOverlay::Flat(overlay) => overlay.leaders(nodes, size, rng),
|
||||||
|
SimulationOverlay::Tree(overlay) => overlay.leaders(nodes, size, rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout<R: Rng>(&self, nodes: &[NodeId], rng: &mut R) -> Layout {
|
||||||
|
match self {
|
||||||
|
SimulationOverlay::Flat(overlay) => overlay.layout(nodes, rng),
|
||||||
|
SimulationOverlay::Tree(overlay) => overlay.layout(nodes, rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Overlay {
|
||||||
|
fn nodes(&self) -> Vec<NodeId>;
|
||||||
|
fn leaders<R: Rng>(
|
||||||
|
&self,
|
||||||
|
nodes: &[NodeId],
|
||||||
|
size: usize,
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Box<dyn Iterator<Item = NodeId>>;
|
||||||
|
fn layout<R: Rng>(&self, nodes: &[NodeId], rng: &mut R) -> Layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes a reference to the simulation_settings and returns a SimulationOverlay instance based
|
||||||
|
// on the overlay settings specified in simulation_settings.
|
||||||
|
pub fn create_overlay(overlay_settings: &OverlaySettings) -> SimulationOverlay {
|
||||||
|
match &overlay_settings {
|
||||||
|
OverlaySettings::Flat => SimulationOverlay::Flat(flat::FlatOverlay::new()),
|
||||||
|
OverlaySettings::Tree(settings) => {
|
||||||
|
SimulationOverlay::Tree(tree::TreeOverlay::new(settings.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -75,7 +75,7 @@ mod tests {
|
|||||||
Node, NodeId, NodeIdExt, OverlayState, SharedState, ViewOverlay,
|
Node, NodeId, NodeIdExt, OverlayState, SharedState, ViewOverlay,
|
||||||
},
|
},
|
||||||
output_processors::OutData,
|
output_processors::OutData,
|
||||||
overlay::{
|
overlay::tests::{
|
||||||
tree::{TreeOverlay, TreeSettings},
|
tree::{TreeOverlay, TreeSettings},
|
||||||
Overlay, SimulationOverlay,
|
Overlay, SimulationOverlay,
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user