diff --git a/.gitignore b/.gitignore index 088ba6ba7..bc3503aea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# VSCode configuration files +.vscode/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c98204be4..3a3660b3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] [dependencies] bellperson = { version = "0.25", default-features = false } ff = { version = "0.13.0", features = ["derive"] } +group = { version = "0.13.0", optional = true } digest = "0.8.1" sha3 = "0.8.2" rayon = "1.3.0" @@ -31,7 +32,9 @@ bincode = "1.2.1" flate2 = "1.0" bitvec = "1.0" byteorder = "1.4.3" -thiserror = "1.0" +thiserror = "1.0" +serde_with = { version = "3.0.0", optional = true } +cfg-if = "1.0.0" [target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] pasta-msm = { version = "0.1.4" } @@ -55,3 +58,7 @@ default = [] portable = ["pasta-msm/portable"] cuda = ["neptune/cuda", "neptune/pasta", "neptune/arity24"] opencl = ["neptune/opencl", "neptune/pasta", "neptune/arity24"] +uncompressed_serde = ["group", "serde_with"] + +[patch.crates-io] +pasta_curves = { git = "https://github.com/lurk-lab/pasta_curves", branch = "uncompressed" } \ No newline at end of file diff --git a/src/provider/pedersen.rs b/src/provider/pedersen.rs index b5a45e197..c706740e9 100644 --- a/src/provider/pedersen.rs +++ b/src/provider/pedersen.rs @@ -15,9 +15,16 @@ use ff::Field; use rayon::prelude::*; use serde::{Deserialize, Serialize}; +#[cfg(feature = "uncompressed_serde")] +use crate::traits::uncompressed::SerializeUncompressed; + /// A type that holds commitment generators #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CommitmentKey { + #[cfg_attr( + feature = "uncompressed_serde", + serde(with = "serde_with::As::>") + )] ck: Vec, _p: PhantomData, } diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 5138cea8d..f028057ca 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -16,6 +16,9 @@ pub mod commitment; use commitment::CommitmentEngineTrait; +#[cfg(feature = "uncompressed_serde")] +use crate::traits::uncompressed::SerializableUncompressed; + /// Represents an element of a group /// This is currently tailored for an elliptic curve group pub trait Group: @@ -55,8 +58,26 @@ pub trait Group: + Serialize + for<'de> Deserialize<'de>; - /// A type representing preprocessed group element - type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>; + cfg_if::cfg_if! { + if #[cfg(feature = "uncompressed_serde")] { + /// A type representing preprocessed group elements + type PreprocessedGroupElement: Clone + + Debug + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de> + + SerializableUncompressed; + } else { + /// A type representing preprocessed group elements + type PreprocessedGroupElement: Clone + + Debug + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de>; + } + } /// A type that represents a circuit-friendly sponge that consumes elements /// from the base field and squeezes out elements of the scalar field @@ -248,3 +269,73 @@ impl> TranscriptReprTrait for &[T] { pub mod circuit; pub mod evaluation; pub mod snark; + +/// Serializers for PreprocessedGroupElement elements that are convertible to +/// an uncompressed representation through [group::UncompressedEncoding] +/// +/// Use `serde(with = "serde_with::As::)` at the point where the `MyType` is +/// used inside a struct or enum definition. Make sure to also enable the feature +/// `uncompressed_serde`. For example: +/// +/// ``` +/// pub struct CommitmentKey { +/// #[cfg_attr( +/// feature = "uncompressed_serde", +/// serde(with = "serde_with::As::>") +/// )] +/// ck: Vec, +/// _p: PhantomData, +/// } +/// ``` +#[cfg(feature = "uncompressed_serde")] +pub(crate) mod uncompressed { + use super::*; + use group::UncompressedEncoding; + use serde::{ + de::{Deserializer, Error as DeError}, + ser::Serializer, + }; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Convenience trait for those instances of UncompressedEncoding which Uncompressed representation type are also serializable + pub trait SerializableUncompressed: UncompressedEncoding { + /// This is intended to be the Uncompressed representation type of the base trait + /// to which we are adding serialization bounds + type Target: Serialize + for<'de> Deserialize<'de>; + } + + /// A blanket implementation that conveys our intended meaning: this works iff the Uncompressed representation type is + /// serializable + impl SerializableUncompressed for T + where + T: UncompressedEncoding, + ::Uncompressed: Serialize + for<'de> Deserialize<'de>, + { + type Target = ::Uncompressed; + } + + pub struct SerializeUncompressed {} + + impl SerializeAs for SerializeUncompressed { + fn serialize_as(source: &T, serializer: S) -> Result + where + S: Serializer, + { + Serialize::serialize(&source.to_uncompressed(), serializer) + } + } + + impl<'de, T: SerializableUncompressed> DeserializeAs<'de, T> for SerializeUncompressed { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let uncompressed = ::deserialize(deserializer)?; + Option::from(T::from_uncompressed(&uncompressed)).ok_or_else(|| { + DeError::custom(format!( + "failed to deserialize group element from uncompressed bytes" + )) + }) + } + } +}