diff --git a/simulations/src/node/mod.rs b/simulations/src/node/mod.rs index d0d656a0..9d27296c 100644 --- a/simulations/src/node/mod.rs +++ b/simulations/src/node/mod.rs @@ -124,3 +124,30 @@ pub trait Node { fn state(&self) -> &Self::State; fn step(&mut self); } + +#[cfg(test)] +impl Node for usize { + type Settings = (); + type State = Self; + + fn new(_rng: &mut R, id: NodeId, _settings: Self::Settings) -> Self { + id.inner() + } + + fn id(&self) -> NodeId { + (*self).into() + } + + fn current_view(&self) -> usize { + *self + } + + fn state(&self) -> &Self::State { + self + } + + fn step(&mut self) { + use std::ops::AddAssign; + self.add_assign(1); + } +} diff --git a/simulations/src/warding/minmax.rs b/simulations/src/warding/minmax.rs new file mode 100644 index 00000000..bfed2080 --- /dev/null +++ b/simulations/src/warding/minmax.rs @@ -0,0 +1,50 @@ +use crate::node::Node; +use crate::warding::{SimulationState, SimulationWard}; +use serde::Deserialize; + +/// MinMaxView. It monitors the gap between a min view and max view, triggers when surpassing +/// the max view - min view is larger than a gap. +#[derive(Debug, Deserialize, Copy, Clone)] +pub struct MinMaxViewWard { + max_gap: usize, +} + +impl SimulationWard for MinMaxViewWard { + type SimulationState = SimulationState; + fn analyze(&mut self, state: &Self::SimulationState) -> bool { + let mut min = usize::MAX; + let mut max = 0; + let nodes = state + .nodes + .read() + .expect("simulations: MinMaxViewWard panic when requiring a read lock"); + for node in nodes.iter() { + let view = node.current_view(); + min = min.min(view); + max = max.max(view); + } + max - min >= self.max_gap + } +} + +#[cfg(test)] +mod test { + use crate::warding::minmax::MinMaxViewWard; + use crate::warding::{SimulationState, SimulationWard}; + use std::sync::{Arc, RwLock}; + + #[test] + fn rebase_threshold() { + let mut minmax = MinMaxViewWard { max_gap: 5 }; + let state = SimulationState { + nodes: Arc::new(RwLock::new(vec![10])), + }; + // we only have one node, so always false + assert!(!minmax.analyze(&state)); + + // push a new node with 10 + state.nodes.write().unwrap().push(20); + // we now have two nodes and the max - min is 10 > max_gap 5, so true + assert!(minmax.analyze(&state)); + } +} diff --git a/simulations/src/warding/mod.rs b/simulations/src/warding/mod.rs index 9887f98c..f0abe64b 100644 --- a/simulations/src/warding/mod.rs +++ b/simulations/src/warding/mod.rs @@ -5,6 +5,7 @@ use serde::Deserialize; // internal use crate::node::Node; +mod minmax; mod ttf; pub struct SimulationState { @@ -21,9 +22,10 @@ pub trait SimulationWard { /// Ward dispatcher /// Enum to avoid Boxing (Box) wards. #[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] pub enum Ward { - #[serde(rename = "time_to_finality")] MaxView(ttf::MaxViewWard), + MinMaxView(minmax::MinMaxViewWard), } impl Ward { @@ -32,6 +34,7 @@ impl Ward { ) -> &mut dyn SimulationWard> { match self { Ward::MaxView(ward) => ward, + Ward::MinMaxView(ward) => ward, } } } diff --git a/simulations/src/warding/ttf.rs b/simulations/src/warding/ttf.rs index d171128c..6ab27694 100644 --- a/simulations/src/warding/ttf.rs +++ b/simulations/src/warding/ttf.rs @@ -23,39 +23,12 @@ impl SimulationWard for MaxViewWard { #[cfg(test)] mod test { - use crate::node::{Node, NodeId}; use crate::warding::ttf::MaxViewWard; use crate::warding::{SimulationState, SimulationWard}; - use rand::Rng; - use std::ops::AddAssign; use std::sync::{Arc, RwLock}; #[test] fn rebase_threshold() { - impl Node for usize { - type Settings = (); - type State = Self; - - fn new(_rng: &mut R, id: NodeId, _settings: Self::Settings) -> Self { - id.inner() - } - - fn id(&self) -> NodeId { - (*self).into() - } - - fn current_view(&self) -> usize { - *self - } - - fn state(&self) -> &Self::State { - self - } - - fn step(&mut self) { - self.add_assign(1); - } - } let mut ttf = MaxViewWard { max_view: 10 }; let node = 11;