1
0
mirror of synced 2025-01-24 06:29:21 +00:00

Add update committees structure for overlay (#295)

* Move roundrobin to leadership module

* Use references in leader selection

* Add membership traits to overlay and create membership module

* Implement committee membership for random beacon state

* Update flat overlay

* Create updateable membership traits and impls

* Update tree overlay

* Update overlay on consensus service

* Update overlay on simulations nomos node

* Update types on tests and modules

* Use chacha for shuffling

* Change to mut slice instead of inner cloning

* Use fisher yates shuffle from scratch

* Stylish and clippy happy
This commit is contained in:
Daniel Sanchez 2023-08-08 10:34:02 +02:00 committed by GitHub
parent ef72c7a110
commit 4bdc3ed15a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 328 additions and 103 deletions

View File

@ -411,11 +411,11 @@ impl<O: Overlay> Carnot<O> {
mod test { mod test {
use std::convert::Infallible; use std::convert::Infallible;
use crate::overlay::{FlatOverlay, FlatOverlaySettings, RoundRobin}; use crate::overlay::{FlatOverlay, FlatOverlaySettings, FreezeMembership, RoundRobin};
use super::*; use super::*;
fn init(nodes: Vec<NodeId>) -> Carnot<FlatOverlay<RoundRobin>> { fn init(nodes: Vec<NodeId>) -> Carnot<FlatOverlay<RoundRobin, FreezeMembership>> {
assert!(!nodes.is_empty()); assert!(!nodes.is_empty());
Carnot::from_genesis( Carnot::from_genesis(
@ -436,7 +436,10 @@ mod test {
) )
} }
fn next_block(engine: &Carnot<FlatOverlay<RoundRobin>>, block: &Block) -> Block { fn next_block(
engine: &Carnot<FlatOverlay<RoundRobin, FreezeMembership>>,
block: &Block,
) -> Block {
let mut next_id = block.id; let mut next_id = block.id;
next_id.0[0] += 1; next_id.0[0] += 1;
@ -454,8 +457,8 @@ mod test {
} }
fn update_leader_selection( fn update_leader_selection(
engine: &Carnot<FlatOverlay<RoundRobin>>, engine: &Carnot<FlatOverlay<RoundRobin, FreezeMembership>>,
) -> Carnot<FlatOverlay<RoundRobin>> { ) -> Carnot<FlatOverlay<RoundRobin, FreezeMembership>> {
engine engine
.update_overlay(|overlay| { .update_overlay(|overlay| {
overlay.update_leader_selection( overlay.update_leader_selection(

View File

@ -1,24 +1,30 @@
use super::LeaderSelection; use super::LeaderSelection;
use crate::overlay::CommitteeMembership;
use crate::{NodeId, Overlay}; use crate::{NodeId, Overlay};
use fraction::{Fraction, ToPrimitive}; use fraction::{Fraction, ToPrimitive};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
const LEADER_SUPER_MAJORITY_THRESHOLD_NUM: u64 = 2; const LEADER_SUPER_MAJORITY_THRESHOLD_NUM: u64 = 2;
const LEADER_SUPER_MAJORITY_THRESHOLD_DEN: u64 = 3; const LEADER_SUPER_MAJORITY_THRESHOLD_DEN: u64 = 3;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
/// Flat overlay with a single committee and round robin leader selection. /// Flat overlay with a single committee and round robin leader selection.
pub struct FlatOverlay<L: LeaderSelection> { pub struct FlatOverlay<L: LeaderSelection, M: CommitteeMembership> {
nodes: Vec<NodeId>, nodes: Vec<NodeId>,
leader: L, leader: L,
leader_threshold: Fraction, leader_threshold: Fraction,
_committee_membership: PhantomData<M>,
} }
impl<L> Overlay for FlatOverlay<L> impl<L, M> Overlay for FlatOverlay<L, M>
where where
L: LeaderSelection + Send + Sync + 'static, L: LeaderSelection + Send + Sync + 'static,
M: CommitteeMembership + Send + Sync + 'static,
{ {
type Settings = FlatOverlaySettings<L>; type Settings = FlatOverlaySettings<L>;
type LeaderSelection = L; type LeaderSelection = L;
type CommitteeMembership = M;
fn new( fn new(
FlatOverlaySettings { FlatOverlaySettings {
@ -36,6 +42,7 @@ where
LEADER_SUPER_MAJORITY_THRESHOLD_DEN, LEADER_SUPER_MAJORITY_THRESHOLD_DEN,
) )
}), }),
_committee_membership: Default::default(),
} }
} }
@ -107,29 +114,12 @@ where
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
}
#[derive(Clone, Debug, Default, PartialEq)] fn update_committees<F, E>(&self, _f: F) -> Result<Self, E>
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] where
pub struct RoundRobin { F: FnOnce(Self::CommitteeMembership) -> Result<Self::CommitteeMembership, E>,
cur: usize, {
} Ok(self.clone())
impl RoundRobin {
pub fn new() -> Self {
Self { cur: 0 }
}
pub fn advance(&self) -> Self {
Self {
cur: (self.cur + 1),
}
}
}
impl LeaderSelection for RoundRobin {
fn next_leader(&self, nodes: &[NodeId]) -> NodeId {
nodes[self.cur % nodes.len()]
} }
} }

View File

@ -0,0 +1,32 @@
// std
// crates
use serde::{Deserialize, Serialize};
// internal
use crate::overlay::LeaderSelection;
use crate::NodeId;
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RoundRobin {
cur: usize,
}
impl RoundRobin {
pub fn new() -> Self {
Self { cur: 0 }
}
pub fn advance(&self) -> Self {
Self {
cur: (self.cur + 1),
}
}
}
impl LeaderSelection for RoundRobin {
fn next_leader(&self, nodes: &[NodeId]) -> NodeId {
nodes[self.cur % nodes.len()]
}
}

View File

@ -0,0 +1,43 @@
// std
// crates
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
// internal
use crate::overlay::CommitteeMembership;
use crate::NodeId;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FisherYatesShuffle {
entropy: [u8; 32],
}
impl FisherYatesShuffle {
pub fn new(entropy: [u8; 32]) -> Self {
Self { entropy }
}
pub fn shuffle<T: Clone>(elements: &mut [T], entropy: [u8; 32]) {
let mut rng = ChaCha20Rng::from_seed(entropy);
// Implementation of fisher yates shuffling
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
for i in (1..elements.len()).rev() {
let j = rng.gen_range(0..=i);
elements.swap(i, j);
}
}
}
impl CommitteeMembership for FisherYatesShuffle {
fn reshape_committees(&self, nodes: &mut [NodeId]) {
FisherYatesShuffle::shuffle(nodes, self.entropy);
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FreezeMembership;
impl CommitteeMembership for FreezeMembership {
fn reshape_committees(&self, _nodes: &mut [NodeId]) {}
}

View File

@ -1,10 +1,14 @@
use super::types::*; use super::types::*;
mod flat_overlay; mod flat_overlay;
mod leadership;
mod membership;
mod random_beacon; mod random_beacon;
mod tree_overlay; mod tree_overlay;
pub use flat_overlay::*; pub use flat_overlay::*;
pub use leadership::*;
pub use membership::*;
pub use random_beacon::*; pub use random_beacon::*;
pub use tree_overlay::*; pub use tree_overlay::*;
@ -13,6 +17,7 @@ use std::marker::Send;
pub trait Overlay: Clone { pub trait Overlay: Clone {
type Settings: Clone + Send + Sync + 'static; type Settings: Clone + Send + Sync + 'static;
type LeaderSelection: LeaderSelection + Clone + Send + Sync + 'static; type LeaderSelection: LeaderSelection + Clone + Send + Sync + 'static;
type CommitteeMembership: CommitteeMembership + Clone + Send + Sync + 'static;
fn new(settings: Self::Settings) -> Self; fn new(settings: Self::Settings) -> Self;
fn root_committee(&self) -> Committee; fn root_committee(&self) -> Committee;
@ -31,8 +36,15 @@ pub trait Overlay: Clone {
fn update_leader_selection<F, E>(&self, f: F) -> Result<Self, E> fn update_leader_selection<F, E>(&self, f: F) -> Result<Self, E>
where where
F: FnOnce(Self::LeaderSelection) -> Result<Self::LeaderSelection, E>; F: FnOnce(Self::LeaderSelection) -> Result<Self::LeaderSelection, E>;
fn update_committees<F, E>(&self, f: F) -> Result<Self, E>
where
F: FnOnce(Self::CommitteeMembership) -> Result<Self::CommitteeMembership, E>;
} }
pub trait LeaderSelection: Clone { pub trait LeaderSelection: Clone {
fn next_leader(&self, nodes: &[NodeId]) -> NodeId; fn next_leader(&self, nodes: &[NodeId]) -> NodeId;
} }
pub trait CommitteeMembership: Clone {
fn reshape_committees(&self, nodes: &mut [NodeId]);
}

View File

@ -1,3 +1,4 @@
use crate::overlay::{CommitteeMembership, FisherYatesShuffle};
use crate::types::*; use crate::types::*;
use bls_signatures::{PrivateKey, PublicKey, Serialize, Signature}; use bls_signatures::{PrivateKey, PublicKey, Serialize, Signature};
use rand::{seq::SliceRandom, SeedableRng}; use rand::{seq::SliceRandom, SeedableRng};
@ -60,6 +61,15 @@ impl RandomBeaconState {
Self::Sad { entropy } Self::Sad { entropy }
} }
pub fn initial_sad_from_entropy(entropy: [u8; 32]) -> Self {
Self::generate_sad(
View::new(-1),
&Self::Sad {
entropy: Box::new(entropy),
},
)
}
pub fn check_advance_happy(&self, rb: RandomBeaconState, view: View) -> Result<Self, Error> { pub fn check_advance_happy(&self, rb: RandomBeaconState, view: View) -> Result<Self, Error> {
let context = view_to_bytes(view); let context = view_to_bytes(view);
match rb { match rb {
@ -97,6 +107,14 @@ impl LeaderSelection for RandomBeaconState {
} }
} }
impl CommitteeMembership for RandomBeaconState {
fn reshape_committees(&self, nodes: &mut [NodeId]) {
let mut seed = [0; 32];
seed.copy_from_slice(&self.entropy().deref()[..32]);
FisherYatesShuffle::shuffle(nodes, seed);
}
}
mod serialize_bls { mod serialize_bls {
use super::*; use super::*;
use serde::{Deserializer, Serializer}; use serde::{Deserializer, Serializer};

View File

@ -1,57 +1,55 @@
use super::tree::Tree; use super::tree::Tree;
use crate::overlay::CommitteeMembership;
use crate::{overlay::LeaderSelection, Committee, NodeId, Overlay}; use crate::{overlay::LeaderSelection, Committee, NodeId, Overlay};
use rand::rngs::StdRng;
use rand::seq::SliceRandom;
use rand::SeedableRng;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TreeOverlaySettings<L: LeaderSelection> { pub struct TreeOverlaySettings<L: LeaderSelection, M: CommitteeMembership> {
pub nodes: Vec<NodeId>, pub nodes: Vec<NodeId>,
pub current_leader: NodeId, pub current_leader: NodeId,
pub entropy: [u8; 32],
pub number_of_committees: usize, pub number_of_committees: usize,
pub leader: L, pub leader: L,
pub committee_membership: M,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TreeOverlay<L> { pub struct TreeOverlay<L, M> {
pub(super) entropy: [u8; 32],
pub(super) number_of_committees: usize, pub(super) number_of_committees: usize,
pub(super) nodes: Vec<NodeId>, pub(super) nodes: Vec<NodeId>,
pub(super) current_leader: NodeId, pub(super) current_leader: NodeId,
pub(super) carnot_tree: Tree, pub(super) carnot_tree: Tree,
pub(super) leader: L, pub(super) leader: L,
pub(super) committee_membership: M,
} }
impl<L> Overlay for TreeOverlay<L> impl<L, M> Overlay for TreeOverlay<L, M>
where where
L: LeaderSelection + Send + Sync + 'static, L: LeaderSelection + Send + Sync + 'static,
M: CommitteeMembership + Send + Sync + 'static,
{ {
type Settings = TreeOverlaySettings<L>; type Settings = TreeOverlaySettings<L, M>;
type LeaderSelection = L; type LeaderSelection = L;
type CommitteeMembership = M;
fn new(settings: Self::Settings) -> Self { fn new(settings: Self::Settings) -> Self {
let TreeOverlaySettings { let TreeOverlaySettings {
mut nodes, mut nodes,
current_leader, current_leader,
entropy,
number_of_committees, number_of_committees,
leader, leader,
committee_membership,
} = settings; } = settings;
let mut rng = StdRng::from_seed(entropy);
// TODO: support custom shuffling algorithm
nodes.shuffle(&mut rng);
committee_membership.reshape_committees(&mut nodes);
let carnot_tree = Tree::new(&nodes, number_of_committees); let carnot_tree = Tree::new(&nodes, number_of_committees);
Self { Self {
entropy,
number_of_committees, number_of_committees,
nodes, nodes,
current_leader, current_leader,
carnot_tree, carnot_tree,
leader, leader,
committee_membership,
} }
} }
@ -131,8 +129,7 @@ where
} }
fn next_leader(&self) -> NodeId { fn next_leader(&self) -> NodeId {
let mut rng = StdRng::from_seed(self.entropy); self.leader.next_leader(&self.nodes)
*self.nodes.choose(&mut rng).unwrap()
} }
fn super_majority_threshold(&self, id: NodeId) -> usize { fn super_majority_threshold(&self, id: NodeId) -> usize {
@ -179,19 +176,36 @@ where
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
fn update_committees<F, E>(&self, f: F) -> Result<Self, E>
where
F: FnOnce(Self::CommitteeMembership) -> Result<Self::CommitteeMembership, E>,
{
f(self.committee_membership.clone()).map(|committee_membership| {
let settings = TreeOverlaySettings {
nodes: self.nodes.clone(),
current_leader: self.current_leader,
number_of_committees: self.number_of_committees,
leader: self.leader.clone(),
committee_membership,
};
Self::new(settings)
})
}
} }
impl<L> TreeOverlay<L> impl<L, M> TreeOverlay<L, M>
where where
L: LeaderSelection + Send + Sync + 'static, L: LeaderSelection + Send + Sync + 'static,
M: CommitteeMembership + Send + Sync + 'static,
{ {
pub fn advance(&self, entropy: [u8; 32], leader: L) -> Self { pub fn advance(&self, leader: L, committee_membership: M) -> Self {
Self::new(TreeOverlaySettings { Self::new(TreeOverlaySettings {
nodes: self.nodes.clone(), nodes: self.nodes.clone(),
current_leader: self.next_leader(), current_leader: self.next_leader(),
entropy,
number_of_committees: self.number_of_committees, number_of_committees: self.number_of_committees,
leader, leader,
committee_membership,
}) })
} }
@ -206,10 +220,12 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::overlay::RoundRobin; use crate::overlay::leadership::RoundRobin;
use crate::overlay::membership::FisherYatesShuffle;
use crate::Overlay; use crate::Overlay;
use super::*; use super::*;
const ENTROPY: [u8; 32] = [0; 32];
#[test] #[test]
fn test_carnot_overlay_leader() { fn test_carnot_overlay_leader() {
@ -217,9 +233,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
nodes: nodes.clone(), nodes: nodes.clone(),
current_leader: nodes[0], current_leader: nodes[0],
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
assert_eq!(*overlay.leader(), nodes[0]); assert_eq!(*overlay.leader(), nodes[0]);
@ -231,13 +247,13 @@ mod tests {
let mut overlay = TreeOverlay::new(TreeOverlaySettings { let mut overlay = TreeOverlay::new(TreeOverlaySettings {
nodes: nodes.clone(), nodes: nodes.clone(),
current_leader: nodes[0], current_leader: nodes[0],
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
let leader = overlay.next_leader(); let leader = overlay.next_leader();
overlay = overlay.advance([1; 32], RoundRobin::new()); overlay = overlay.advance(RoundRobin::new(), FisherYatesShuffle::new(ENTROPY));
assert_eq!(leader, *overlay.leader()); assert_eq!(leader, *overlay.leader());
} }
@ -248,9 +264,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
current_leader: nodes[0], current_leader: nodes[0],
nodes, nodes,
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
let mut expected_root = Committee::new(); let mut expected_root = Committee::new();
@ -266,9 +282,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
current_leader: nodes[0], current_leader: nodes[0],
nodes, nodes,
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
let mut leaf_committees = overlay let mut leaf_committees = overlay
@ -296,9 +312,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
current_leader: nodes[0], current_leader: nodes[0],
nodes, nodes,
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
assert_eq!(overlay.super_majority_threshold(overlay.nodes[8]), 0); assert_eq!(overlay.super_majority_threshold(overlay.nodes[8]), 0);
@ -310,9 +326,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
current_leader: nodes[0], current_leader: nodes[0],
nodes, nodes,
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
assert_eq!(overlay.super_majority_threshold(overlay.nodes[0]), 3); assert_eq!(overlay.super_majority_threshold(overlay.nodes[0]), 3);
@ -324,9 +340,9 @@ mod tests {
let overlay = TreeOverlay::new(TreeOverlaySettings { let overlay = TreeOverlay::new(TreeOverlaySettings {
nodes: nodes.clone(), nodes: nodes.clone(),
current_leader: nodes[0], current_leader: nodes[0],
entropy: [0; 32],
number_of_committees: 3, number_of_committees: 3,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: FisherYatesShuffle::new(ENTROPY),
}); });
assert_eq!( assert_eq!(

View File

@ -1,5 +1,6 @@
use std::{collections::HashSet, panic}; use std::{collections::HashSet, panic};
use consensus_engine::overlay::FreezeMembership;
use consensus_engine::{ use consensus_engine::{
overlay::{FlatOverlay, FlatOverlaySettings, RoundRobin}, overlay::{FlatOverlay, FlatOverlaySettings, RoundRobin},
*, *,
@ -13,7 +14,7 @@ use crate::fuzz::transition::Transition;
// This is called as SUT (System Under Test). // This is called as SUT (System Under Test).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ConsensusEngineTest { pub struct ConsensusEngineTest {
pub engine: Carnot<FlatOverlay<RoundRobin>>, pub engine: Carnot<FlatOverlay<RoundRobin, FreezeMembership>>,
} }
impl ConsensusEngineTest { impl ConsensusEngineTest {

View File

@ -1,7 +1,7 @@
mod tx; mod tx;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use consensus_engine::overlay::{FlatOverlay, RoundRobin}; use consensus_engine::overlay::{FlatOverlay, RandomBeaconState, RoundRobin};
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
use metrics::{backend::map::MapMetricsBackend, types::MetricsData, MetricsService}; use metrics::{backend::map::MapMetricsBackend, types::MetricsData, MetricsService};
use nomos_consensus::{ use nomos_consensus::{
@ -38,7 +38,7 @@ pub type Carnot = CarnotConsensus<
MockPool<Tx>, MockPool<Tx>,
MempoolWakuAdapter<Tx>, MempoolWakuAdapter<Tx>,
MockFountain, MockFountain,
FlatOverlay<RoundRobin>, FlatOverlay<RoundRobin, RandomBeaconState>,
>; >;
#[derive(Services)] #[derive(Services)]

View File

@ -0,0 +1,53 @@
use std::convert::Infallible;
// std
use std::error::Error;
use std::hash::Hash;
// crates
// internal
use consensus_engine::overlay::{
CommitteeMembership, Error as RandomBeaconError, FreezeMembership, RandomBeaconState,
};
use consensus_engine::TimeoutQc;
use nomos_core::block::Block;
pub trait UpdateableCommitteeMembership: CommitteeMembership {
type Error: Error;
fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self,
block: &Block<Tx>,
) -> Result<Self, Self::Error>;
fn on_timeout_qc_received(&self, qc: &TimeoutQc) -> Result<Self, Self::Error>;
}
impl UpdateableCommitteeMembership for FreezeMembership {
type Error = Infallible;
fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self,
_block: &Block<Tx>,
) -> Result<Self, Self::Error> {
Ok(Self)
}
fn on_timeout_qc_received(&self, _qc: &TimeoutQc) -> Result<Self, Self::Error> {
Ok(Self)
}
}
impl UpdateableCommitteeMembership for RandomBeaconState {
type Error = RandomBeaconError;
fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self,
block: &Block<Tx>,
) -> Result<Self, Self::Error> {
self.check_advance_happy(block.beacon().clone(), block.header().parent_qc.view())
}
fn on_timeout_qc_received(&self, qc: &TimeoutQc) -> Result<Self, Self::Error> {
Ok(Self::generate_sad(qc.view(), self))
}
}

View File

@ -1,5 +1,6 @@
use consensus_engine::overlay::RoundRobin;
use consensus_engine::{ use consensus_engine::{
overlay::{Error as RandomBeaconError, LeaderSelection, RandomBeaconState, RoundRobin}, overlay::{Error as RandomBeaconError, LeaderSelection, RandomBeaconState},
TimeoutQc, TimeoutQc,
}; };
use nomos_core::block::Block; use nomos_core::block::Block;
@ -10,9 +11,9 @@ pub trait UpdateableLeaderSelection: LeaderSelection {
fn on_new_block_received<Tx: Hash + Clone + Eq>( fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self, &self,
block: Block<Tx>, block: &Block<Tx>,
) -> Result<Self, Self::Error>; ) -> Result<Self, Self::Error>;
fn on_timeout_qc_received(&self, qc: TimeoutQc) -> Result<Self, Self::Error>; fn on_timeout_qc_received(&self, qc: &TimeoutQc) -> Result<Self, Self::Error>;
} }
impl UpdateableLeaderSelection for RoundRobin { impl UpdateableLeaderSelection for RoundRobin {
@ -20,12 +21,12 @@ impl UpdateableLeaderSelection for RoundRobin {
fn on_new_block_received<Tx: Hash + Clone + Eq>( fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self, &self,
_block: Block<Tx>, _block: &Block<Tx>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
Ok(self.advance()) Ok(self.advance())
} }
fn on_timeout_qc_received(&self, _qc: TimeoutQc) -> Result<Self, Self::Error> { fn on_timeout_qc_received(&self, _qc: &TimeoutQc) -> Result<Self, Self::Error> {
Ok(self.advance()) Ok(self.advance())
} }
} }
@ -35,13 +36,13 @@ impl UpdateableLeaderSelection for RandomBeaconState {
fn on_new_block_received<Tx: Hash + Clone + Eq>( fn on_new_block_received<Tx: Hash + Clone + Eq>(
&self, &self,
block: Block<Tx>, block: &Block<Tx>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
self.check_advance_happy(block.beacon().clone(), block.header().parent_qc.view()) self.check_advance_happy(block.beacon().clone(), block.header().parent_qc.view())
// TODO: check random beacon public keys is leader id // TODO: check random beacon public keys is leader id
} }
fn on_timeout_qc_received(&self, qc: TimeoutQc) -> Result<Self, Self::Error> { fn on_timeout_qc_received(&self, qc: &TimeoutQc) -> Result<Self, Self::Error> {
Ok(Self::generate_sad(qc.view(), self)) Ok(Self::generate_sad(qc.view(), self))
} }
} }

View File

@ -1,3 +1,4 @@
pub mod committee_membership;
pub mod leader_selection; pub mod leader_selection;
pub mod network; pub mod network;
mod tally; mod tally;
@ -33,6 +34,7 @@ use consensus_engine::{
}; };
use task_manager::TaskManager; use task_manager::TaskManager;
use crate::committee_membership::UpdateableCommitteeMembership;
use nomos_core::block::Block; use nomos_core::block::Block;
use nomos_core::fountain::FountainCode; use nomos_core::fountain::FountainCode;
use nomos_core::tx::Transaction; use nomos_core::tx::Transaction;
@ -144,6 +146,7 @@ where
M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static, M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static,
O: Overlay + Debug + Send + Sync + 'static, O: Overlay + Debug + Send + Sync + 'static,
O::LeaderSelection: UpdateableLeaderSelection, O::LeaderSelection: UpdateableLeaderSelection,
O::CommitteeMembership: UpdateableCommitteeMembership,
{ {
fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> { fn init(service_state: ServiceStateHandle<Self>) -> Result<Self, overwatch_rs::DynError> {
let network_relay = service_state.overwatch_handle.relay(); let network_relay = service_state.overwatch_handle.relay();
@ -283,6 +286,7 @@ where
M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static, M: MempoolAdapter<Tx = P::Tx> + Send + Sync + 'static,
O: Overlay + Debug + Send + Sync + 'static, O: Overlay + Debug + Send + Sync + 'static,
O::LeaderSelection: UpdateableLeaderSelection, O::LeaderSelection: UpdateableLeaderSelection,
O::CommitteeMembership: UpdateableCommitteeMembership,
{ {
fn process_message(carnot: &Carnot<O>, msg: ConsensusMsg) { fn process_message(carnot: &Carnot<O>, msg: ConsensusMsg) {
match msg { match msg {
@ -426,9 +430,13 @@ where
tally_settings, tally_settings,
), ),
); );
new_state = Self::update_leader_selection(new_state, |leader_selection| { new_state = Self::update_overlay(
leader_selection.on_new_block_received(original_block) new_state,
}); |leader_selection| leader_selection.on_new_block_received(&original_block),
|committee_membership| {
committee_membership.on_new_block_received(&original_block)
},
);
} else { } else {
task_manager.push(block.view, async move { task_manager.push(block.view, async move {
if let Some(block) = stream.next().await { if let Some(block) = stream.next().await {
@ -514,9 +522,11 @@ where
Self::gather_new_views(adapter, self_committee, timeout_qc.clone(), tally_settings), Self::gather_new_views(adapter, self_committee, timeout_qc.clone(), tally_settings),
); );
if carnot.current_view() != new_state.current_view() { if carnot.current_view() != new_state.current_view() {
new_state = Self::update_leader_selection(new_state, |leader_selection| { new_state = Self::update_overlay(
leader_selection.on_timeout_qc_received(timeout_qc) new_state,
}); |leader_selection| leader_selection.on_timeout_qc_received(&timeout_qc),
|committee_membership| committee_membership.on_timeout_qc_received(&timeout_qc),
);
} }
(new_state, None) (new_state, None)
} }
@ -723,16 +733,41 @@ where
fn update_leader_selection< fn update_leader_selection<
E: std::error::Error, E: std::error::Error,
U: FnOnce(O::LeaderSelection) -> Result<O::LeaderSelection, E>, Fl: FnOnce(O::LeaderSelection) -> Result<O::LeaderSelection, E>,
>( >(
carnot: Carnot<O>, carnot: Carnot<O>,
f: U, leader_selection_f: Fl,
) -> Carnot<O> { ) -> Carnot<O> {
carnot carnot
.update_overlay(|overlay| overlay.update_leader_selection(f)) .update_overlay(|overlay| overlay.update_leader_selection(leader_selection_f))
// TODO: remove unwrap
.unwrap() .unwrap()
} }
fn update_committee_membership<
E: std::error::Error,
Fm: FnOnce(O::CommitteeMembership) -> Result<O::CommitteeMembership, E>,
>(
carnot: Carnot<O>,
committee_membership_f: Fm,
) -> Carnot<O> {
carnot
.update_overlay(|overlay| overlay.update_committees(committee_membership_f))
.unwrap()
}
fn update_overlay<
El: std::error::Error,
Em: std::error::Error,
Fl: FnOnce(O::LeaderSelection) -> Result<O::LeaderSelection, El>,
Fm: FnOnce(O::CommitteeMembership) -> Result<O::CommitteeMembership, Em>,
>(
carnot: Carnot<O>,
leader_selection_f: Fl,
committee_membership_f: Fm,
) -> Carnot<O> {
let carnot = Self::update_leader_selection(carnot, leader_selection_f);
Self::update_committee_membership(carnot, committee_membership_f)
}
} }
async fn handle_output<A, F, Tx>(adapter: &A, fountain: &F, node_id: NodeId, output: Output<Tx>) async fn handle_output<A, F, Tx>(adapter: &A, fountain: &F, node_id: NodeId, output: Output<Tx>)

View File

@ -1,5 +1,6 @@
use consensus_engine::overlay::RandomBeaconState;
use consensus_engine::{ use consensus_engine::{
overlay::{FlatOverlay, RoundRobin, TreeOverlay}, overlay::{FlatOverlay, FreezeMembership, RoundRobin, TreeOverlay},
NodeId, NodeId,
}; };
use rand::Rng; use rand::Rng;
@ -26,37 +27,41 @@ pub fn to_overlay_node<R: Rng>(
leader: RoundRobin::new(), leader: RoundRobin::new(),
leader_super_majority_threshold: None, leader_super_majority_threshold: None,
}; };
Box::new(CarnotNode::<FlatOverlay<RoundRobin>>::new( Box::new(
node_id, CarnotNode::<FlatOverlay<RoundRobin, FreezeMembership>>::new(
CarnotSettings::new( node_id,
settings.node_settings.timeout, CarnotSettings::new(
settings.record_settings.clone(), settings.node_settings.timeout,
settings.record_settings.clone(),
),
overlay_settings,
genesis,
network_interface,
&mut rng,
), ),
overlay_settings, )
genesis,
network_interface,
&mut rng,
))
} }
simulations::settings::OverlaySettings::Tree(tree_settings) => { simulations::settings::OverlaySettings::Tree(tree_settings) => {
let overlay_settings = consensus_engine::overlay::TreeOverlaySettings { let overlay_settings = consensus_engine::overlay::TreeOverlaySettings {
nodes, nodes,
current_leader: leader, current_leader: leader,
entropy: [0; 32],
number_of_committees: tree_settings.number_of_committees, number_of_committees: tree_settings.number_of_committees,
leader: RoundRobin::new(), leader: RoundRobin::new(),
committee_membership: RandomBeaconState::initial_sad_from_entropy([0; 32]),
}; };
Box::new(CarnotNode::<TreeOverlay<RoundRobin>>::new( Box::new(
node_id, CarnotNode::<TreeOverlay<RoundRobin, RandomBeaconState>>::new(
CarnotSettings::new( node_id,
settings.node_settings.timeout, CarnotSettings::new(
settings.record_settings.clone(), settings.node_settings.timeout,
settings.record_settings.clone(),
),
overlay_settings,
genesis,
network_interface,
&mut rng,
), ),
overlay_settings, )
genesis,
network_interface,
&mut rng,
))
} }
} }
} }

View File

@ -24,6 +24,7 @@ use consensus_engine::overlay::RandomBeaconState;
use consensus_engine::{ use consensus_engine::{
Block, BlockId, Carnot, Committee, Overlay, Payload, Qc, StandardQc, TimeoutQc, View, Vote, Block, BlockId, Carnot, Committee, Overlay, Payload, Qc, StandardQc, TimeoutQc, View, Vote,
}; };
use nomos_consensus::committee_membership::UpdateableCommitteeMembership;
use nomos_consensus::network::messages::{ProposalChunkMsg, TimeoutQcMsg}; use nomos_consensus::network::messages::{ProposalChunkMsg, TimeoutQcMsg};
use nomos_consensus::{ use nomos_consensus::{
leader_selection::UpdateableLeaderSelection, leader_selection::UpdateableLeaderSelection,
@ -224,7 +225,12 @@ pub struct CarnotNode<O: Overlay> {
step_duration: Duration, step_duration: Duration,
} }
impl<L: UpdateableLeaderSelection, O: Overlay<LeaderSelection = L>> CarnotNode<O> { impl<
L: UpdateableLeaderSelection,
M: UpdateableCommitteeMembership,
O: Overlay<LeaderSelection = L, CommitteeMembership = M>,
> CarnotNode<O>
{
pub fn new<R: Rng>( pub fn new<R: Rng>(
id: consensus_engine::NodeId, id: consensus_engine::NodeId,
settings: CarnotSettings, settings: CarnotSettings,
@ -342,8 +348,13 @@ impl<L: UpdateableLeaderSelection, O: Overlay<LeaderSelection = L>> CarnotNode<O
if self.engine.current_view() != new.current_view() { if self.engine.current_view() != new.current_view() {
new = new new = new
.update_overlay(|overlay| { .update_overlay(|overlay| {
overlay.update_leader_selection(|leader_selection| { let overlay = overlay
leader_selection.on_new_block_received(block.clone()) .update_leader_selection(|leader_selection| {
leader_selection.on_new_block_received(&block)
})
.expect("Leader selection update should succeed");
overlay.update_committees(|committee_membership| {
committee_membership.on_new_block_received(&block)
}) })
}) })
.unwrap_or(new); .unwrap_or(new);
@ -481,7 +492,12 @@ impl<L: UpdateableLeaderSelection, O: Overlay<LeaderSelection = L>> CarnotNode<O
} }
} }
impl<L: UpdateableLeaderSelection, O: Overlay<LeaderSelection = L>> Node for CarnotNode<O> { impl<
L: UpdateableLeaderSelection,
M: UpdateableCommitteeMembership,
O: Overlay<LeaderSelection = L, CommitteeMembership = M>,
> Node for CarnotNode<O>
{
type Settings = CarnotSettings; type Settings = CarnotSettings;
type State = CarnotState; type State = CarnotState;