From 4aac4dcbae7ec4d96a40923703aa57479a091c6c Mon Sep 17 00:00:00 2001 From: Federico Franzoni <8609060+fed-franz@users.noreply.github.com> Date: Sat, 31 Aug 2024 11:43:55 +0200 Subject: [PATCH] consensus: generate committees when starting from loaded iteration - add generate_committee to IterationCtx - move committee generation code from phase `run` function - remove save_committee - move get_sortition_config to IterationCtx --- consensus/src/consensus.rs | 37 ++++++++++++++ consensus/src/execution_ctx.rs | 19 +------ consensus/src/iteration_ctx.rs | 90 +++++++++++++++++++++++++++++++++- consensus/src/phase.rs | 57 --------------------- 4 files changed, 127 insertions(+), 76 deletions(-) diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 8f84add44c..b264f336d2 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -10,6 +10,7 @@ use crate::operations::Operations; use crate::phase::Phase; use node_data::message::{AsyncQueue, Message, Topics}; +use node_data::StepName; use crate::execution_ctx::ExecutionCtx; use crate::proposal; @@ -184,6 +185,35 @@ impl Consensus { ru.base_timeouts.clone(), ); + // TODO: load save iteration + let saved_iter = 0; + + // If starting from `saved_iter`, we regenerate all committees + // in case they are needed to process past-iteration messages in + // Emergency Mode + if saved_iter != 0 { + while iter <= saved_iter { + iter_ctx.on_begin(iter); + iter_ctx.generate_committee( + StepName::Proposal, + provisioners.as_ref(), + ru.seed(), + ); + iter_ctx.generate_committee( + StepName::Validation, + provisioners.as_ref(), + ru.seed(), + ); + iter_ctx.generate_committee( + StepName::Ratification, + provisioners.as_ref(), + ru.seed(), + ); + } + + iter = saved_iter + 1; + } + loop { Self::consensus_delay().await; @@ -197,6 +227,13 @@ impl Consensus { // phase. phase.reinitialize(msg, ru.round, iter).await; + // Generate step committee + iter_ctx.generate_committee( + step_name, + provisioners.as_ref(), + ru.seed(), + ); + // Construct phase execution context let ctx = ExecutionCtx::new( &mut iter_ctx, diff --git a/consensus/src/execution_ctx.rs b/consensus/src/execution_ctx.rs index 016fcf05af..92b2bb4c40 100644 --- a/consensus/src/execution_ctx.rs +++ b/consensus/src/execution_ctx.rs @@ -13,7 +13,7 @@ use crate::queue::MsgRegistry; use crate::step_votes_reg::SafeAttestationInfoRegistry; use crate::user::committee::Committee; use crate::user::provisioners::Provisioners; -use crate::user::sortition; + use node_data::bls::PublicKeyBytes; use node_data::ledger::Block; @@ -106,10 +106,6 @@ impl<'a, T: Operations + 'static> ExecutionCtx<'a, T> { committee.is_member(&self.round_update.pubkey_bls) } - pub(crate) fn save_committee(&mut self, step: u8, committee: Committee) { - self.iter_ctx.committees.insert(step, committee); - } - pub(crate) fn get_current_committee(&self) -> Option<&Committee> { self.iter_ctx.committees.get_committee(self.step()) } @@ -456,19 +452,6 @@ impl<'a, T: Operations + 'static> ExecutionCtx<'a, T> { None } - pub fn get_sortition_config( - &self, - exclusion: Vec, - ) -> sortition::Config { - sortition::Config::new( - self.round_update.seed(), - self.round_update.round, - self.iteration, - self.step_name(), - exclusion, - ) - } - /// Reports step elapsed time to the client async fn report_elapsed_time(&mut self) { let elapsed = self diff --git a/consensus/src/iteration_ctx.rs b/consensus/src/iteration_ctx.rs index 071fcb4685..ce4cac489d 100644 --- a/consensus/src/iteration_ctx.rs +++ b/consensus/src/iteration_ctx.rs @@ -7,15 +7,18 @@ use crate::commons::{RoundUpdate, TimeoutSet}; use std::cmp; -use crate::config::{MAX_STEP_TIMEOUT, TIMEOUT_INCREASE}; +use crate::config::{CONSENSUS_MAX_ITER, MAX_STEP_TIMEOUT, TIMEOUT_INCREASE}; use crate::msg_handler::HandleMsgOutput; use crate::msg_handler::MsgHandler; use crate::user::committee::Committee; +use crate::user::provisioners::Provisioners; +use crate::user::sortition; use crate::{ratification, validation}; use node_data::bls::PublicKeyBytes; +use node_data::ledger::Seed; use node_data::message::Message; use std::collections::HashMap; use std::ops::Add; @@ -131,6 +134,91 @@ impl IterationCtx { .expect("valid timeout per step") } + fn get_sortition_config( + &self, + seed: Seed, + step_name: StepName, + exclusion: Vec, + ) -> sortition::Config { + sortition::Config::new( + seed, self.round, self.iter, step_name, exclusion, + ) + } + + pub(crate) fn generate_committee( + &mut self, + step_name: StepName, + provisioners: &Provisioners, + seed: Seed, + ) { + let iteration = self.iter; + let step = step_name.to_step(iteration); + + // Check if this committee has been already generated + if self.committees.get_committee(step).is_some() { + return; + } + + // Fill up exclusion list + // + // We exclude the generators for the current iteration and the next one + // to avoid conflict of interests + let exclusion = match step_name { + StepName::Proposal => vec![], + _ => { + let mut exclusion_list = vec![]; + // Exclude generator for current iteration + let cur_generator = self + .get_generator(iteration) + .expect("Proposal committee to be already generated"); + + exclusion_list.push(cur_generator); + + // Exclude generator for next iteration + if iteration < CONSENSUS_MAX_ITER { + let next_generator = + self.get_generator(iteration + 1).expect( + "Next Proposal committee to be already generated", + ); + + exclusion_list.push(next_generator); + } + + exclusion_list + } + }; + + // Generate the committee for the current step + // If the step is Proposal, the only extracted member is the generator + // For Validation and Ratification steps, extracted members are + // delegated to vote on the candidate block + let step_committee = Committee::new( + provisioners, + &self.get_sortition_config(seed, step_name, exclusion), + ); + + if let StepName::Proposal = step_name { + if iteration < CONSENSUS_MAX_ITER { + let mut cfg_next_iteration = + self.get_sortition_config(seed, step_name, vec![]); + cfg_next_iteration.step = + StepName::Proposal.to_step(iteration + 1); + + let next_iteration_generator = + Committee::new(provisioners, &cfg_next_iteration); + self.committees + .insert(cfg_next_iteration.step, next_iteration_generator); + } + } + + debug!( + event = "committee_generated", + members = format!("{}", &step_committee) + ); + + self.committees.insert(step, step_committee); + } + pub(crate) fn get_generator(&self, iter: u8) -> Option { let step = StepName::Proposal.to_step(iter); self.committees diff --git a/consensus/src/phase.rs b/consensus/src/phase.rs index bb0a159311..53e99e37f7 100644 --- a/consensus/src/phase.rs +++ b/consensus/src/phase.rs @@ -5,10 +5,8 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database}; -use crate::config::CONSENSUS_MAX_ITER; use crate::execution_ctx::ExecutionCtx; use crate::operations::Operations; -use crate::user::committee::Committee; use crate::{proposal, ratification, validation}; use node_data::message::Message; use node_data::StepName; @@ -58,64 +56,9 @@ impl Phase { ) -> Result { ctx.set_start_time(); - let step_name = ctx.step_name(); let timeout = ctx.iter_ctx.get_timeout(ctx.step_name()); debug!(event = "execute_step", ?timeout); - let exclusion = match step_name { - StepName::Proposal => vec![], - _ => { - let mut exclusion_list = vec![]; - let generator = ctx - .iter_ctx - .get_generator(ctx.iteration) - .expect("Proposal committee to be already generated"); - - exclusion_list.push(generator); - - if ctx.iteration < CONSENSUS_MAX_ITER { - let next_generator = - ctx.iter_ctx.get_generator(ctx.iteration + 1).expect( - "Next Proposal committee to be already generated", - ); - - exclusion_list.push(next_generator); - } - - exclusion_list - } - }; - - // Perform deterministic_sortition to generate committee of size=N. - // The extracted members are the provisioners eligible to vote on this - // particular round and step. In the context of Proposal phase, - // the extracted member is the one eligible to generate the candidate - // block. - let step_committee = Committee::new( - ctx.provisioners, - &ctx.get_sortition_config(exclusion), - ); - - if let StepName::Proposal = step_name { - if ctx.iteration < CONSENSUS_MAX_ITER { - let mut cfg_next_iteration = ctx.get_sortition_config(vec![]); - cfg_next_iteration.step = - StepName::Proposal.to_step(ctx.iteration + 1); - - ctx.save_committee( - cfg_next_iteration.step, - Committee::new(ctx.provisioners, &cfg_next_iteration), - ); - } - } - - debug!( - event = "committee_generated", - members = format!("{}", &step_committee) - ); - - ctx.save_committee(ctx.step(), step_committee); - // Execute step await_phase!(self, run(ctx)) }