From c40b8972fbff3813ca8ac58a9141b3ffc7c24fc4 Mon Sep 17 00:00:00 2001 From: Demilade Sonuga Date: Fri, 6 Dec 2024 13:47:33 +0100 Subject: [PATCH] Added serde feature --- .github/workflows/dusk_ci.yml | 6 ++ Cargo.toml | 13 ++++ src/dusk.rs | 3 + src/dusk/serde_support.rs | 116 ++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 src/dusk/serde_support.rs diff --git a/.github/workflows/dusk_ci.yml b/.github/workflows/dusk_ci.yml index 86b5b79..e65f3b8 100644 --- a/.github/workflows/dusk_ci.yml +++ b/.github/workflows/dusk_ci.yml @@ -26,3 +26,9 @@ jobs: uses: dusk-network/.github/.github/workflows/run-tests.yml@main with: test_flags: --no-default-features + + test_serde: + name: Serde tests + uses: dusk-network/.github/.github/workflows/run-tests.yml@main + with: + test_flags: --features=serde diff --git a/Cargo.toml b/Cargo.toml index 4a3645e..6994758 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,18 @@ optional = true default-features = false # End Dusk dependendencies +[dependencies.serde] +version = "1.0" +optional = true + +[dependencies.serde_json] +version = "1.0" +optional = true + +[dependencies.hex] +version = "0.4" +optional = true + [dev-dependencies] criterion = "0.3" csv = ">= 1.0, < 1.2" # csv 1.2 has MSRV 1.60 @@ -86,6 +98,7 @@ default = ["alloc", "bits"] alloc = ["ff/alloc", "group/alloc"] bits = ["ff/bits"] rkyv-impl = ["bytecheck", "dusk-bls12_381/rkyv-impl", "rkyv"] +serde = ["dep:serde", "serde_json", "dusk-bls12_381/serde", "hex"] [[bench]] name = "fq_bench" diff --git a/src/dusk.rs b/src/dusk.rs index f311142..84d2ead 100644 --- a/src/dusk.rs +++ b/src/dusk.rs @@ -7,6 +7,9 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "serde")] +mod serde_support; + use core::ops::Mul; use ff::Field; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; diff --git a/src/dusk/serde_support.rs b/src/dusk/serde_support.rs new file mode 100644 index 0000000..dbeabb5 --- /dev/null +++ b/src/dusk/serde_support.rs @@ -0,0 +1,116 @@ +extern crate alloc; + +use alloc::string::String; + +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{AffinePoint, ExtendedPoint, Fr}; + +impl Serialize for AffinePoint { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = hex::encode(self.to_bytes()); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for AffinePoint { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let bytes: [u8; 32] = hex::decode(s) + .map_err(Error::custom)? + .try_into() + .map_err(|_| { + Error::custom( + "Failed to deserialize AffinePoint: invalid byte length", + ) + })?; + AffinePoint::from_bytes(bytes) + .into_option() + .ok_or(Error::custom( + "Failed to deserialize AffinePoint: invalid AffinePoint", + )) + } +} + +impl Serialize for ExtendedPoint { + fn serialize( + &self, + serializer: S, + ) -> Result { + AffinePoint::from(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for ExtendedPoint { + fn deserialize>( + deserializer: D, + ) -> Result { + AffinePoint::deserialize(deserializer).map(Into::into) + } +} + +impl Serialize for Fr { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = hex::encode(self.to_bytes()); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for Fr { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let bytes: [u8; 32] = hex::decode(s) + .map_err(Error::custom)? + .try_into() + .map_err(|_| { + Error::custom("Failed to deserialize Fr: invalid byte length") + })?; + Fr::from_bytes(&bytes) + .into_option() + .ok_or(Error::custom("Failed to deserialize Fr: invalid Fr")) + } +} + +#[cfg(test)] +mod tests { + use ff::Field; + use group::Group; + + use crate::{AffinePoint, ExtendedPoint, Fr}; + + #[test] + fn affine_point() { + let point = AffinePoint::identity(); + let ser = serde_json::to_string(&point).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(point, deser); + } + + #[test] + fn extended_point() { + let mut rng = rand_core::OsRng; + let point = ExtendedPoint::random(&mut rng); + let ser = serde_json::to_string(&point).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(point, deser); + } + + #[test] + fn fr() { + let mut rng = rand_core::OsRng; + let fr = Fr::random(&mut rng); + let ser = serde_json::to_string(&fr).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(fr, deser); + } +}