diff --git a/consensus/src/aggregator.rs b/consensus/src/aggregator.rs index c39909ae92..2b04f3c3bb 100644 --- a/consensus/src/aggregator.rs +++ b/consensus/src/aggregator.rs @@ -4,6 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use crate::config::EMERGENCY_MODE_ITERATION_THRESHOLD; use crate::user::cluster::Cluster; use crate::user::committee::Committee; use dusk_bytes::Serializable; @@ -17,7 +18,7 @@ use node_data::message::StepMessage; use std::collections::{BTreeMap, HashMap}; use std::fmt; use thiserror::Error; -use tracing::{debug, error}; +use tracing::{debug, error, warn}; /// Aggregator collects votes for Validation and Ratification steps by /// mapping step numbers and [StepVote] to both an aggregated signature and a @@ -81,8 +82,19 @@ impl Aggregator { v: &V, ) -> Result<(StepVotes, bool), AggregatorError> { let sign_info = v.sign_info(); + + let iter = v.header().iteration; + + let emergency = iter >= EMERGENCY_MODE_ITERATION_THRESHOLD; + let msg_step = v.get_step(); let vote = v.vote(); + if emergency && !vote.is_valid() { + warn!( + "Vote {vote:?} for iter {iter} skipped due to emergency mode", + ); + return Ok((StepVotes::default(), false)); + } let signature = sign_info.signature.inner(); let signer = &sign_info.signer; @@ -109,14 +121,18 @@ impl Aggregator { return Err(AggregatorError::DuplicatedVote); } - // Check if the provisioner voted for a different result - let voters_list = self.uniqueness.entry(msg_step).or_default(); - match voters_list.get(signer.bytes()) { - None => voters_list.insert(*signer.bytes(), v.clone()), - Some(prev_vote) => { - return Err(AggregatorError::ConflictingVote(prev_vote.clone())) - } - }; + if v.header().iteration < EMERGENCY_MODE_ITERATION_THRESHOLD { + // Check if the provisioner voted for a different result + let voters_list = self.uniqueness.entry(msg_step).or_default(); + match voters_list.get(signer.bytes()) { + None => voters_list.insert(*signer.bytes(), v.clone()), + Some(prev_vote) => { + return Err(AggregatorError::ConflictingVote( + prev_vote.clone(), + )) + } + }; + } // Aggregate Signatures aggr_sign.add(signature)?; @@ -134,6 +150,8 @@ impl Aggregator { event = "vote aggregated", ?vote, from = signer.to_bs58(), + iter = v.header().iteration, + step = ?V::STEP_NAME, added, total, majority = committee.majority_quorum(), @@ -158,6 +176,8 @@ impl Aggregator { tracing::info!( event = "quorum reached", ?vote, + iter = v.header().iteration, + step = ?V::STEP_NAME, total, target = quorum_target, bitset, diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 9c818ffa50..a7f5b67270 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database, QuorumMsgSender, RoundUpdate}; -use crate::config::CONSENSUS_MAX_ITER; +use crate::config::{CONSENSUS_MAX_ITER, EMERGENCY_MODE_ITERATION_THRESHOLD}; use crate::operations::Operations; use crate::phase::Phase; @@ -20,9 +20,9 @@ use tracing::{debug, error, info, Instrument}; use crate::iteration_ctx::IterationCtx; use crate::step_votes_reg::AttInfoRegistry; -use std::env; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::{cmp, env}; use tokio::sync::{oneshot, Mutex}; use tokio::task::JoinHandle; @@ -187,6 +187,9 @@ impl Consensus { let (prev_block_hash, saved_iter) = db.lock().await.get_last_iter().await; + let saved_iter = + cmp::min(EMERGENCY_MODE_ITERATION_THRESHOLD, saved_iter); + if ru.hash() == prev_block_hash { // If starting from `saved_iter`, we regenerate all committees // in case they are needed to process past-iteration messages in diff --git a/consensus/src/execution_ctx.rs b/consensus/src/execution_ctx.rs index 41da044e29..342a122a3c 100644 --- a/consensus/src/execution_ctx.rs +++ b/consensus/src/execution_ctx.rs @@ -268,11 +268,13 @@ impl<'a, T: Operations + 'static> ExecutionCtx<'a, T> { } Payload::ValidationResult(validation_result) => { - self.try_cast_ratification_vote( - msg_iteration, - validation_result, - ) - .await + if let QuorumType::Valid = validation_result.quorum() { + self.try_cast_ratification_vote( + msg_iteration, + validation_result, + ) + .await + } } _ => { // Not supported. diff --git a/consensus/src/ratification/handler.rs b/consensus/src/ratification/handler.rs index 462bff3d99..56cd6d6460 100644 --- a/consensus/src/ratification/handler.rs +++ b/consensus/src/ratification/handler.rs @@ -120,7 +120,7 @@ impl MsgHandler for RatificationHandler { event = "Cannot collect vote", ?error, from = p.sign_info().signer.to_bs58(), - ?p.vote, + vote = ?p.vote, msg_step = p.get_step(), msg_round = p.header().round, ); diff --git a/consensus/src/ratification/step.rs b/consensus/src/ratification/step.rs index 998d5f28c7..6aba0935d0 100644 --- a/consensus/src/ratification/step.rs +++ b/consensus/src/ratification/step.rs @@ -5,12 +5,13 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, RoundUpdate}; +use crate::config::EMERGENCY_MODE_ITERATION_THRESHOLD; use crate::execution_ctx::ExecutionCtx; use crate::operations::Operations; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; use crate::ratification::handler; -use node_data::message::payload::{self, ValidationResult}; +use node_data::message::payload::{self, QuorumType, ValidationResult}; use node_data::message::{AsyncQueue, Message, Payload, StepMessage}; use node_data::{get_current_timestamp, message}; use std::sync::Arc; @@ -35,11 +36,15 @@ impl RatificationStep { let msg = Message::from(ratification); - // Publish ratification vote - info!(event = "send_vote", validation_bitset = result.sv().bitset); + if result.quorum() == QuorumType::Valid + || iteration < EMERGENCY_MODE_ITERATION_THRESHOLD + { + // Publish ratification vote + info!(event = "send_vote", validation_bitset = result.sv().bitset); - // Publish - outbound.try_send(msg.clone()); + // Publish + outbound.try_send(msg.clone()); + } msg } diff --git a/consensus/src/validation/handler.rs b/consensus/src/validation/handler.rs index 5da200c260..3d98f137af 100644 --- a/consensus/src/validation/handler.rs +++ b/consensus/src/validation/handler.rs @@ -149,7 +149,7 @@ impl MsgHandler for ValidationHandler { event = "Cannot collect vote", ?error, from = p.sign_info().signer.to_bs58(), - ?p.vote, + vote = ?p.vote, msg_step = p.get_step(), msg_round = p.header().round, ); diff --git a/consensus/src/validation/step.rs b/consensus/src/validation/step.rs index c8ef8b15e8..0a13759e0d 100644 --- a/consensus/src/validation/step.rs +++ b/consensus/src/validation/step.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, RoundUpdate}; -use crate::config; +use crate::config::{self, EMERGENCY_MODE_ITERATION_THRESHOLD}; use crate::execution_ctx::ExecutionCtx; use crate::operations::{Operations, Voter}; use crate::validation::handler; @@ -126,11 +126,13 @@ impl ValidationStep { info!(event = "send_vote", vote = ?validation.vote); let msg = Message::from(validation); - // Publish - outbound.try_send(msg.clone()); + if vote.is_valid() || iteration < EMERGENCY_MODE_ITERATION_THRESHOLD { + // Publish + outbound.try_send(msg.clone()); - // Register my vote locally - inbound.try_send(msg); + // Register my vote locally + inbound.try_send(msg); + } } async fn call_vst( diff --git a/node-data/src/ledger/faults.rs b/node-data/src/ledger/faults.rs index a2806b7ff0..90be7e5230 100644 --- a/node-data/src/ledger/faults.rs +++ b/node-data/src/ledger/faults.rs @@ -71,6 +71,8 @@ pub enum InvalidFault { PrevHashMismatch, #[error("Iteration mismatch")] IterationMismatch, + #[error("Faults related to emergency iteration")] + EmergencyIteration, #[error("Round mismatch")] RoundMismatch, #[error("Invalid Signature {0}")] diff --git a/node-data/src/message.rs b/node-data/src/message.rs index 2814e70c8a..d5e2579597 100644 --- a/node-data/src/message.rs +++ b/node-data/src/message.rs @@ -512,6 +512,9 @@ pub mod payload { } impl Vote { + pub fn is_valid(&self) -> bool { + matches!(self, Vote::Valid(_)) + } pub fn size(&self) -> usize { const ENUM_BYTE: usize = 1; @@ -654,11 +657,8 @@ pub mod payload { }) } } - #[derive(Clone, Copy, Debug, Default)] - #[cfg_attr( - any(feature = "faker", test), - derive(fake::Dummy, Eq, PartialEq) - )] + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] + #[cfg_attr(any(feature = "faker", test), derive(fake::Dummy))] pub enum QuorumType { /// Supermajority of Valid votes Valid = 0, diff --git a/node/src/chain/header_validation.rs b/node/src/chain/header_validation.rs index e49d9b0da5..439cbf6cc3 100644 --- a/node/src/chain/header_validation.rs +++ b/node/src/chain/header_validation.rs @@ -8,7 +8,10 @@ use crate::database; use crate::database::Ledger; use anyhow::anyhow; use dusk_bytes::Serializable; -use dusk_consensus::config::{MINIMUM_BLOCK_TIME, RELAX_ITERATION_THRESHOLD}; +use dusk_consensus::config::{ + EMERGENCY_MODE_ITERATION_THRESHOLD, MINIMUM_BLOCK_TIME, + RELAX_ITERATION_THRESHOLD, +}; use dusk_consensus::operations::Voter; use dusk_consensus::quorum::verifiers; use dusk_consensus::quorum::verifiers::QuorumResult; @@ -297,6 +300,9 @@ pub async fn verify_faults( ) -> Result<(), InvalidFault> { for f in faults { let fault_header = f.validate(current_height)?; + if fault_header.iteration >= EMERGENCY_MODE_ITERATION_THRESHOLD { + return Err(InvalidFault::EmergencyIteration); + } db.read() .await .view(|db| {