Skip to content

Commit

Permalink
Added serde feature
Browse files Browse the repository at this point in the history
  • Loading branch information
d-sonuga committed Dec 12, 2024
1 parent 40e9c78 commit 0592a2a
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/dusk_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
name: Nightly tests std
uses: dusk-network/.github/.github/workflows/run-tests.yml@main
with:
test_flags: --features=zeroize
test_flags: --features=zeroize,serde

test_nightly_no_std:
name: Nightly tests no_std
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add serde `Serialize` and `Deserialize` implementations for `Fr`, `AffinePoint` and `ExtendedPoint` [#143]
- Add `serde`, `hex` and `serde_json` optional dependencies [#143]
- Add `serde` feature [#143]

## [0.14.1] - 2024-04-24

### Added
Expand Down Expand Up @@ -222,6 +228,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Initial fork from [`zkcrypto/jubjub`]

<!-- ISSUES -->
[#143]: https://github.com/dusk-network/jubjub/issues/143
[#137]: https://github.com/dusk-network/jubjub/issues/137
[#135]: https://github.com/dusk-network/jubjub/issues/135
[#129]: https://github.com/dusk-network/jubjub/issues/129
Expand All @@ -244,6 +251,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#31]: https://github.com/dusk-network/jubjub/issues/31
[#25]: https://github.com/dusk-network/jubjub/issues/25


<!-- VERSIONS -->
[Unreleased]: https://github.com/dusk-network/jubjub/compare/v0.14.1...HEAD
[0.14.1]: https://github.com/dusk-network/jubjub/compare/v0.14.0...v0.14.1
Expand Down
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ default-features = false
version = "1"
optional = true
default-features = false

[dependencies.serde]
version = "1.0"
optional = true

[dependencies.serde_json]
version = "1.0"
optional = true

[dependencies.hex]
version = "0.4"
optional = true
# End Dusk dependendencies

[dev-dependencies]
Expand All @@ -81,11 +93,15 @@ default-features = false
[dev-dependencies.blake2]
version = "0.9"

[dev-dependencies.rand]
version = "0.8"

[features]
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", "hex"]

[[bench]]
name = "fq_bench"
Expand Down
3 changes: 3 additions & 0 deletions src/dusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
99 changes: 99 additions & 0 deletions src/dusk/serde_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
extern crate alloc;

use alloc::string::{String, ToString};

use dusk_bytes::Serializable;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};

use crate::{AffinePoint, ExtendedPoint};

impl Serialize for AffinePoint {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
let s = hex::encode(self.to_bytes());
serializer.serialize_str(&s)
}
}

impl<'de> Deserialize<'de> for AffinePoint {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
let decoded = hex::decode(&s).map_err(Error::custom)?;
let decoded_len = decoded.len();
let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| {
Error::invalid_length(decoded_len, &Self::SIZE.to_string().as_str())
})?;
AffinePoint::from_bytes(bytes)
.into_option()
.ok_or(Error::custom(
"Failed to deserialize AffinePoint: invalid AffinePoint",
))
}
}

impl Serialize for ExtendedPoint {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
AffinePoint::from(self).serialize(serializer)
}
}

impl<'de> Deserialize<'de> for ExtendedPoint {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
AffinePoint::deserialize(deserializer).map(Into::into)
}
}

#[cfg(test)]
mod tests {
use group::Group;
use rand::rngs::StdRng;
use rand::SeedableRng;

use crate::{AffinePoint, ExtendedPoint};

#[test]
fn serde_affine_point() {
let mut rng = StdRng::seed_from_u64(0xdead);
let point = ExtendedPoint::random(&mut rng);
let point = AffinePoint::from(point);
let ser = serde_json::to_string(&point).unwrap();
let deser = serde_json::from_str(&ser).unwrap();
assert_eq!(point, deser);
}

#[test]
fn serde_wrong_encoded() {
let wrong_encoded = "wrong-encoded";

let affine_point: Result<AffinePoint, _> =
serde_json::from_str(&wrong_encoded);
assert!(affine_point.is_err());
}

#[test]
fn serde_too_long_encoded() {
let length_33_enc = "\"e4ab9de40283a85d6ea0cd0120500697d8b01c71b7b4b520292252d20937000631\"";

let affine_point: Result<AffinePoint, _> =
serde_json::from_str(&length_33_enc);
assert!(affine_point.is_err());
}

#[test]
fn serde_too_short_encoded() {
let length_31_enc = "\"1751c37a1dca7aa4c048fcc6177194243edc3637bae042e167e4285945e046\"";

let affine_point: Result<AffinePoint, _> =
serde_json::from_str(&length_31_enc);
assert!(affine_point.is_err());
}
}
72 changes: 72 additions & 0 deletions src/fr/dusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,48 @@ impl Serializable<32> for Fr {
}
}

#[cfg(feature = "serde")]
mod serde_support {
extern crate alloc;

use alloc::string::{String, ToString};

use dusk_bytes::Serializable;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use super::Fr;

impl Serialize for Fr {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
let s = hex::encode(self.to_bytes());
serializer.serialize_str(&s)
}
}

impl<'de> Deserialize<'de> for Fr {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
let decoded = hex::decode(s).map_err(Error::custom)?;
let decoded_len = decoded.len();
let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| {
Error::invalid_length(
decoded_len,
&Self::SIZE.to_string().as_str(),
)
})?;
Fr::from_bytes(&bytes)
.into_option()
.ok_or(Error::custom("Failed to deserialize Fr: invalid Fr"))
}
}
}

#[test]
fn w_naf_3() {
let scalar = Fr::from(1122334455u64);
Expand Down Expand Up @@ -366,3 +408,33 @@ fn test_zeroize() {
scalar.zeroize();
assert_eq!(scalar, Fr::zero());
}

#[cfg(feature = "serde")]
#[test]
fn serde_fr() {
use ff::Field;
use rand::rngs::StdRng;
use rand::SeedableRng;

let mut rng = StdRng::seed_from_u64(0xdead);
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);

// Should error when the encoding is wrong
let wrong_encoded = "wrong-encoded";
let fr: Result<Fr, _> = serde_json::from_str(&wrong_encoded);
assert!(fr.is_err());

// Should error when the input is too long
let length_33_enc = "\"e4ab9de40283a85d6ea0cd0120500697d8b01c71b7b4b520292252d20937000631\"";
let fr: Result<Fr, _> = serde_json::from_str(&length_33_enc);
assert!(fr.is_err());

// Should error when the input is too short
let length_31_enc =
"\"1751c37a1dca7aa4c048fcc6177194243edc3637bae042e167e4285945e046\"";
let fr: Result<Fr, _> = serde_json::from_str(&length_31_enc);
assert!(fr.is_err());
}

0 comments on commit 0592a2a

Please sign in to comment.