Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add serde feature #141

Merged
merged 1 commit into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
d-sonuga marked this conversation as resolved.
Show resolved Hide resolved
# 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);
d-sonuga marked this conversation as resolved.
Show resolved Hide resolved
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());
}