diff --git a/consensus-engine/src/lib.rs b/consensus-engine/src/lib.rs index dfe745ce..26113b7f 100644 --- a/consensus-engine/src/lib.rs +++ b/consensus-engine/src/lib.rs @@ -406,7 +406,7 @@ mod test { *nodes.first().unwrap(), Block { view: 0, - id: [0; 32], + id: BlockId::zeros(), parent_qc: Qc::Standard(StandardQc::genesis()), leader_proof: LeaderProof::LeaderId { leader_id: *nodes.first().unwrap(), @@ -422,7 +422,7 @@ mod test { fn next_block(engine: &Carnot>, block: &Block) -> Block { let mut next_id = block.id; - next_id[0] += 1; + next_id.0[0] += 1; Block { view: block.view + 1, @@ -498,10 +498,10 @@ mod test { fn receive_block_with_unknown_parent() { let engine = init(vec![NodeId::new([0; 32])]); let mut parent_block_id = engine.genesis_block().id; - parent_block_id[0] += 1; // generate an unknown parent block ID + parent_block_id.0[0] += 1; // generate an unknown parent block ID let block = Block { view: engine.current_view() + 1, - id: [1; 32], + id: BlockId::new([1; 32]), parent_qc: Qc::Standard(StandardQc { view: engine.current_view(), id: parent_block_id, @@ -601,7 +601,7 @@ mod test { // a future block should be rejected let future_block = Block { - id: [10; 32], + id: BlockId::new([10; 32]), view: 11, // a future view parent_qc: Qc::Aggregated(AggregateQc { view: 10, @@ -619,7 +619,7 @@ mod test { // a past block should be also rejected let mut past_block = block1; // with the same view as block1 - past_block.id = [10; 32]; + past_block.id = BlockId::new([10; 32]); assert!(engine.receive_block(past_block).is_err()); } @@ -696,7 +696,7 @@ mod test { sender: NodeId::new([0; 32]), high_qc: StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, timeout_qc: None }), @@ -719,7 +719,7 @@ mod test { 1, StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); @@ -744,7 +744,7 @@ mod test { 1, StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); @@ -771,7 +771,7 @@ mod test { 1, StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); @@ -813,7 +813,7 @@ mod test { 1, StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); @@ -826,7 +826,7 @@ mod test { 2, StandardQc { view: 0, // genesis - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); diff --git a/consensus-engine/src/types.rs b/consensus-engine/src/types.rs index c51f0c4f..1158523e 100644 --- a/consensus-engine/src/types.rs +++ b/consensus-engine/src/types.rs @@ -8,9 +8,10 @@ mod committee; pub use committee::{Committee, CommitteeId}; mod node_id; pub use node_id::NodeId; +mod block_id; +pub use block_id::BlockId; pub type View = i64; -pub type BlockId = [u8; 32]; /// The way the consensus engine communicates with the rest of the system is by returning /// actions to be performed. @@ -116,7 +117,7 @@ impl Block { pub fn genesis() -> Self { Self { - id: [0; 32], + id: BlockId::zeros(), view: 0, parent_qc: Qc::Standard(StandardQc::genesis()), leader_proof: LeaderProof::LeaderId { @@ -144,7 +145,7 @@ impl StandardQc { pub fn genesis() -> Self { Self { view: -1, - id: [0; 32], + id: BlockId::zeros(), } } } @@ -197,11 +198,11 @@ mod test { fn standard_qc() { let standard_qc = StandardQc { view: 10, - id: [0; 32], + id: BlockId::zeros(), }; let qc = Qc::Standard(standard_qc.clone()); assert_eq!(qc.view(), 10); - assert_eq!(qc.block(), [0; 32]); + assert_eq!(qc.block(), BlockId::new([0; 32])); assert_eq!(qc.high_qc(), standard_qc); } @@ -211,12 +212,12 @@ mod test { view: 20, high_qc: StandardQc { view: 10, - id: [0; 32], + id: BlockId::zeros(), }, }; let qc = Qc::Aggregated(aggregated_qc.clone()); assert_eq!(qc.view(), 20); - assert_eq!(qc.block(), [0; 32]); + assert_eq!(qc.block(), BlockId::new([0; 32])); assert_eq!(qc.high_qc(), aggregated_qc.high_qc); } @@ -226,26 +227,26 @@ mod test { 2, StandardQc { view: 1, - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); assert_eq!(timeout_qc.view(), 2); assert_eq!(timeout_qc.high_qc().view, 1); - assert_eq!(timeout_qc.high_qc().id, [0; 32]); + assert_eq!(timeout_qc.high_qc().id, BlockId::new([0; 32])); assert_eq!(timeout_qc.sender(), NodeId::new([0; 32])); let timeout_qc = TimeoutQc::new( 2, StandardQc { view: 2, - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); assert_eq!(timeout_qc.view(), 2); assert_eq!(timeout_qc.high_qc().view, 2); - assert_eq!(timeout_qc.high_qc().id, [0; 32]); + assert_eq!(timeout_qc.high_qc().id, BlockId::new([0; 32])); assert_eq!(timeout_qc.sender(), NodeId::new([0; 32])); } @@ -258,7 +259,7 @@ mod test { 1, StandardQc { view: 2, - id: [0; 32], + id: BlockId::zeros(), }, NodeId::new([0; 32]), ); diff --git a/consensus-engine/src/types/block_id.rs b/consensus-engine/src/types/block_id.rs new file mode 100644 index 00000000..b054f935 --- /dev/null +++ b/consensus-engine/src/types/block_id.rs @@ -0,0 +1,50 @@ +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct BlockId(pub(crate) [u8; 32]); + +impl BlockId { + pub const fn new(val: [u8; 32]) -> Self { + Self(val) + } + + pub const fn zeros() -> Self { + Self([0; 32]) + } + + /// Returns a random block id, only avaliable with feature `simulation` or test + #[cfg(any(test, feature = "simulation"))] + pub fn random(rng: &mut R) -> Self { + let mut bytes = [0u8; 32]; + rng.fill_bytes(&mut bytes); + Self(bytes) + } +} + +impl From<[u8; 32]> for BlockId { + fn from(id: [u8; 32]) -> Self { + Self(id) + } +} + +impl From<&[u8; 32]> for BlockId { + fn from(id: &[u8; 32]) -> Self { + Self(*id) + } +} + +impl From for [u8; 32] { + fn from(id: BlockId) -> Self { + id.0 + } +} + +impl core::fmt::Display for BlockId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "0x")?; + for v in self.0 { + write!(f, "{:02x}", v)?; + } + Ok(()) + } +} diff --git a/consensus-engine/tests/fuzz/ref_state.rs b/consensus-engine/tests/fuzz/ref_state.rs index b52ea677..6aeef37d 100644 --- a/consensus-engine/tests/fuzz/ref_state.rs +++ b/consensus-engine/tests/fuzz/ref_state.rs @@ -42,7 +42,7 @@ impl ReferenceStateMachine for RefState { fn init_state() -> BoxedStrategy { let genesis_block = Block { view: 0, - id: [0; 32], + id: BlockId::zeros(), parent_qc: Qc::Standard(StandardQc::genesis()), leader_proof: LEADER_PROOF.clone(), }; @@ -332,7 +332,7 @@ impl RefState { let current_view = self.current_view(); Just(Transition::ReceiveSafeBlock(Block { - id: rand::thread_rng().gen(), + id: BlockId::random(&mut rand::thread_rng()), view: current_view + 1, parent_qc: Qc::Aggregated(AggregateQc { high_qc: self.high_qc(), @@ -395,7 +395,7 @@ impl RefState { fn consecutive_block(parent: &Block) -> Block { Block { // use rand because we don't want this to be shrinked by proptest - id: rand::thread_rng().gen(), + id: BlockId::random(&mut rand::thread_rng()), view: parent.view + 1, parent_qc: Qc::Standard(StandardQc { view: parent.view, diff --git a/consensus-engine/tests/fuzz/sut.rs b/consensus-engine/tests/fuzz/sut.rs index 38550bc9..a3d132be 100644 --- a/consensus-engine/tests/fuzz/sut.rs +++ b/consensus-engine/tests/fuzz/sut.rs @@ -22,7 +22,7 @@ impl ConsensusEngineTest { NodeId::new([0; 32]), Block { view: 0, - id: [0; 32], + id: BlockId::zeros(), parent_qc: Qc::Standard(StandardQc::genesis()), leader_proof: LeaderProof::LeaderId { leader_id: NodeId::new([0; 32]), diff --git a/nomos-core/src/block.rs b/nomos-core/src/block.rs index 7962cc0e..2b86b049 100644 --- a/nomos-core/src/block.rs +++ b/nomos-core/src/block.rs @@ -5,6 +5,7 @@ use core::hash::Hash; // crates use crate::wire; use bytes::Bytes; +pub use consensus_engine::BlockId; use consensus_engine::{LeaderProof, NodeId, Qc, View}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -20,9 +21,6 @@ pub struct Block { beacon: RandomBeaconState, } -/// Identifier of a block -pub type BlockId = [u8; 32]; - impl Block { pub fn new( view: View, @@ -33,7 +31,7 @@ impl Block { ) -> Self { let transactions = txs.collect(); let header = consensus_engine::Block { - id: [0; 32], + id: BlockId::zeros(), view, parent_qc, leader_proof: LeaderProof::LeaderId { @@ -74,7 +72,7 @@ pub fn block_id_from_wire_content::new(); hasher.update(bytes); - hasher.finalize().into() + BlockId::new(hasher.finalize().into()) } impl Block { diff --git a/nomos-services/consensus/src/lib.rs b/nomos-services/consensus/src/lib.rs index 46311ff2..92c52dae 100644 --- a/nomos-services/consensus/src/lib.rs +++ b/nomos-services/consensus/src/lib.rs @@ -177,7 +177,7 @@ where let overlay = O::new(overlay_settings); let genesis = consensus_engine::Block { - id: [0; 32], + id: BlockId::zeros(), view: 0, parent_qc: Qc::Standard(StandardQc::genesis()), leader_proof: LeaderProof::LeaderId { @@ -560,7 +560,7 @@ where let mut output = None; mempool_relay .send(MempoolMsg::View { - ancestor_hint: [0; 32], + ancestor_hint: BlockId::zeros(), reply_channel, }) .await @@ -864,16 +864,16 @@ mod tests { highest_voted_view: -1, local_high_qc: StandardQc { view: 0, - id: [0; 32], + id: BlockId::zeros(), }, safe_blocks: HashMap::from([( - [0; 32], + BlockId::zeros(), Block { - id: [0; 32], + id: BlockId::zeros(), view: 0, parent_qc: Qc::Standard(StandardQc { view: 0, - id: [0; 32], + id: BlockId::zeros(), }), leader_proof: LeaderProof::LeaderId { leader_id: NodeId::new([0; 32]), @@ -881,7 +881,7 @@ mod tests { }, )]), last_view_timeout_qc: None, - committed_blocks: vec![[0; 32]], + committed_blocks: vec![BlockId::zeros()], }; let serialized = serde_json::to_string(&info).unwrap();