diff --git a/Cargo.toml b/Cargo.toml index c3a0590f..be01f92a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "nomos-da/full-replication", "nomos-http-api", "nomos-cli", + "nomos-utils", "nodes/nomos-node", "nodes/mixnode", "simulations", diff --git a/consensus-engine/Cargo.toml b/consensus-engine/Cargo.toml index 9c3938eb..1c3ab499 100644 --- a/consensus-engine/Cargo.toml +++ b/consensus-engine/Cargo.toml @@ -15,10 +15,11 @@ rand = "0.8" rand_chacha = "0.3" thiserror = "1" fraction = { version = "0.13" } +nomos-utils = { path = "../nomos-utils", optional = true } [features] default = [] -serde = ["dep:serde"] +serde = ["dep:serde", "nomos-utils/serde"] simulation = [] [dev-dependencies] diff --git a/consensus-engine/src/types/block_id.rs b/consensus-engine/src/types/block_id.rs index 1d5a697f..3b542d49 100644 --- a/consensus-engine/src/types/block_id.rs +++ b/consensus-engine/src/types/block_id.rs @@ -1,8 +1,23 @@ #[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]); +#[cfg(feature = "serde")] +impl serde::Serialize for BlockId { + fn serialize(&self, serializer: S) -> Result { + nomos_utils::serde::serialize_bytes_array(self.0, serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::de::Deserialize<'de> for BlockId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + nomos_utils::serde::deserialize_bytes_array(deserializer).map(Self) + } +} + impl BlockId { pub const fn new(val: [u8; 32]) -> Self { Self(val) diff --git a/consensus-engine/src/types/committee.rs b/consensus-engine/src/types/committee.rs index ca7a7ca0..4ee8c270 100644 --- a/consensus-engine/src/types/committee.rs +++ b/consensus-engine/src/types/committee.rs @@ -3,9 +3,25 @@ use std::collections::BTreeSet; use crate::NodeId; #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CommitteeId(pub(crate) [u8; 32]); +#[cfg(feature = "serde")] +impl serde::Serialize for CommitteeId { + fn serialize(&self, serializer: S) -> Result { + nomos_utils::serde::serialize_bytes_array(self.0, serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::de::Deserialize<'de> for CommitteeId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + nomos_utils::serde::deserialize_bytes_array(deserializer).map(Self) + } +} + impl CommitteeId { pub const fn new(val: [u8; 32]) -> Self { Self(val) diff --git a/consensus-engine/src/types/node_id.rs b/consensus-engine/src/types/node_id.rs index e2c8c48d..48748049 100644 --- a/consensus-engine/src/types/node_id.rs +++ b/consensus-engine/src/types/node_id.rs @@ -1,8 +1,23 @@ #[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(transparent))] pub struct NodeId(pub(crate) [u8; 32]); +#[cfg(feature = "serde")] +impl serde::Serialize for NodeId { + fn serialize(&self, serializer: S) -> Result { + nomos_utils::serde::serialize_bytes_array(self.0, serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::de::Deserialize<'de> for NodeId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + nomos_utils::serde::deserialize_bytes_array(deserializer).map(Self) + } +} + impl NodeId { pub const fn new(val: [u8; 32]) -> Self { Self(val) diff --git a/nomos-services/consensus/src/lib.rs b/nomos-services/consensus/src/lib.rs index 5cd8bb19..8c84bc47 100644 --- a/nomos-services/consensus/src/lib.rs +++ b/nomos-services/consensus/src/lib.rs @@ -1060,6 +1060,22 @@ pub struct CarnotInfo { pub committed_blocks: Vec, } +async fn get_mempool_contents( + mempool: OutboundRelay>, +) -> Result + Send>, tokio::sync::oneshot::error::RecvError> { + let (reply_channel, rx) = tokio::sync::oneshot::channel(); + + mempool + .send(MempoolMsg::View { + ancestor_hint: BlockId::zeros(), + reply_channel, + }) + .await + .unwrap_or_else(|(e, _)| eprintln!("Could not get transactions from mempool {e}")); + + rx.await +} + #[cfg(test)] mod tests { use consensus_engine::Block; @@ -1098,26 +1114,10 @@ mod tests { eprintln!("{serialized}"); assert_eq!( serialized, - r#"{"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"current_view":1,"highest_voted_view":-1,"local_high_qc":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"safe_blocks":[[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],{"view":0,"parent_qc":{"Standard":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"leader_proof":{"LeaderId":{"leader_id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}}]],"last_view_timeout_qc":null,"committed_blocks":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"# + r#"{"id":"0x0000000000000000000000000000000000000000000000000000000000000000","current_view":1,"highest_voted_view":-1,"local_high_qc":{"view":0,"id":"0x0000000000000000000000000000000000000000000000000000000000000000"},"safe_blocks":[["0x0000000000000000000000000000000000000000000000000000000000000000",{"view":0,"parent_qc":{"Standard":{"view":0,"id":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"leader_proof":{"LeaderId":{"leader_id":"0x0000000000000000000000000000000000000000000000000000000000000000"}}}]],"last_view_timeout_qc":null,"committed_blocks":["0x0000000000000000000000000000000000000000000000000000000000000000"]}"# ); let deserialized: CarnotInfo = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, info); } } - -async fn get_mempool_contents( - mempool: OutboundRelay>, -) -> Result + Send>, tokio::sync::oneshot::error::RecvError> { - let (reply_channel, rx) = tokio::sync::oneshot::channel(); - - mempool - .send(MempoolMsg::View { - ancestor_hint: BlockId::zeros(), - reply_channel, - }) - .await - .unwrap_or_else(|(e, _)| eprintln!("Could not get transactions from mempool {e}")); - - rx.await -} diff --git a/nomos-utils/Cargo.toml b/nomos-utils/Cargo.toml new file mode 100644 index 00000000..3b5f223c --- /dev/null +++ b/nomos-utils/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nomos-utils" +version = "0.1.0" +edition = "2021" + +[features] +serde = ["dep:serde"] + +[dependencies] +const-hex = "1" +serde = { version = "1.0", optional = true } \ No newline at end of file diff --git a/nomos-utils/src/lib.rs b/nomos-utils/src/lib.rs new file mode 100644 index 00000000..356e4689 --- /dev/null +++ b/nomos-utils/src/lib.rs @@ -0,0 +1,67 @@ +#[cfg(feature = "serde")] +pub mod serde { + fn serialize_human_readable_bytes_array( + src: [u8; N], + serializer: S, + ) -> Result { + use serde::Serialize; + const_hex::const_encode::(&src) + .as_str() + .serialize(serializer) + } + + pub fn serialize_bytes_array( + src: [u8; N], + serializer: S, + ) -> Result { + if serializer.is_human_readable() { + serialize_human_readable_bytes_array(src, serializer) + } else { + serializer.serialize_bytes(&src) + } + } + + fn deserialize_human_readable_bytes_array<'de, const N: usize, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result<[u8; N], D::Error> { + use serde::Deserialize; + use std::borrow::Cow; + let s: Cow = Cow::deserialize(deserializer)?; + let mut output = [0u8; N]; + const_hex::decode_to_slice(s.as_ref(), &mut output) + .map(|_| output) + .map_err(::custom) + } + + fn deserialize_human_unreadable_bytes_array< + 'de, + const N: usize, + D: serde::Deserializer<'de>, + >( + deserializer: D, + ) -> Result<[u8; N], D::Error> { + use serde::Deserialize; + <&[u8]>::deserialize(deserializer).and_then(|bytes| { + if bytes.len() != N { + Err(::invalid_length( + bytes.len(), + &format!("{N}").as_str(), + )) + } else { + let mut output = [0u8; N]; + output.copy_from_slice(bytes); + Ok(output) + } + }) + } + + pub fn deserialize_bytes_array<'de, const N: usize, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result<[u8; N], D::Error> { + if deserializer.is_human_readable() { + deserialize_human_readable_bytes_array(deserializer) + } else { + deserialize_human_unreadable_bytes_array(deserializer) + } + } +}