diff --git a/tests/src/tests/unhappy.rs b/tests/src/tests/unhappy.rs index 376b4225..358d05f9 100644 --- a/tests/src/tests/unhappy.rs +++ b/tests/src/tests/unhappy.rs @@ -1,10 +1,12 @@ -use consensus_engine::View; +use consensus_engine::{Block, NodeId, TimeoutQc, View}; use fraction::Fraction; use futures::stream::{self, StreamExt}; +use nomos_consensus::CarnotInfo; use std::collections::HashSet; use tests::{ConsensusConfig, MixNode, Node, NomosNode, SpawnConfig}; const TARGET_VIEW: View = View::new(20); +const DUMMY_NODE_ID: NodeId = NodeId::new([0u8; 32]); #[tokio::test] async fn ten_nodes_one_down() { @@ -46,30 +48,75 @@ async fn ten_nodes_one_down() { .collect::>() .await; - // check that they have the same block - let target_blocks = infos - .iter() - .map(|i| i.safe_blocks.values().find(|b| b.view == TARGET_VIEW)) + let target_block = assert_block_consensus(&infos, TARGET_VIEW); + + // If no node has the target block, check that TARGET_VIEW was reached by timeout_qc. + if target_block.is_none() { + println!("No node has the block with {TARGET_VIEW:?}. Checking timeout_qcs..."); + assert_timeout_qc_consensus(&infos, TARGET_VIEW.prev()); + } +} + +// Check if all nodes have the same block at the specific view. +fn assert_block_consensus<'a>( + consensus_infos: impl IntoIterator, + view: View, +) -> Option { + let blocks = consensus_infos + .into_iter() + .map(|i| i.safe_blocks.values().find(|b| b.view == view)) .collect::>(); // Every nodes must have the same target block (Some(block)) // , or no node must have it (None). - assert_eq!(target_blocks.len(), 1); + assert_eq!( + blocks.len(), + 1, + "multiple blocks found at {:?}: {:?}", + view, + blocks + ); - // If no node has the target block, check that TARGET_VIEW was reached by timeout_qc. - let target_block = target_blocks.iter().next().unwrap(); - if target_block.is_none() { - println!("No node has the block with {TARGET_VIEW:?}. Checking timeout_qcs..."); - - let timeout_qcs = infos - .iter() - .map(|i| i.last_view_timeout_qc.clone()) - .collect::>(); - assert_eq!(timeout_qcs.len(), 1); - - let timeout_qc = timeout_qcs.iter().next().unwrap().clone(); - assert!(timeout_qc.is_some()); - // NOTE: This check could be failed if other timeout_qc had occured before `infos` were gathered. - // But it should be okay as long as the `timeout` is not too short. - assert_eq!(timeout_qc.unwrap().view(), TARGET_VIEW.prev()); - } + blocks.iter().next().unwrap().cloned() +} + +// Check if all nodes have the same timeout_qc at the specific view. +fn assert_timeout_qc_consensus<'a>( + consensus_infos: impl IntoIterator, + view: View, +) -> TimeoutQc { + let timeout_qcs = consensus_infos + .into_iter() + .map(|i| { + i.last_view_timeout_qc.clone().map(|timeout_qc| { + // Masking the `sender` field because we want timeout_qcs from different + // senders to be considered the same if all other fields are the same. + TimeoutQc::new( + timeout_qc.view(), + timeout_qc.high_qc().clone(), + DUMMY_NODE_ID, + ) + }) + }) + .collect::>(); + assert_eq!( + timeout_qcs.len(), + 1, + "multiple timeout_qcs found at {:?}: {:?}", + view, + timeout_qcs + ); + + let timeout_qc = timeout_qcs + .iter() + .next() + .unwrap() + .clone() + .expect("collected timeout_qc shouldn't be None"); + + // NOTE: This check could be failed if other timeout_qcs had occured + // before `consensus_infos` were gathered. + // But it should be okay as long as the `timeout` is not too short. + assert_eq!(timeout_qc.view(), view); + + timeout_qc }