From 13964b6f168d872a56de6742ca1d36a84fce10c3 Mon Sep 17 00:00:00 2001 From: Samuel Burnham <45365069+samuelburnham@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:01:08 -0500 Subject: [PATCH] Add serde proof serialization (#123) * Bump commit. * Bump commit. * (WIP) Add serde support * Minor fixes * Use neptune const generics * Use git patches * Impl serde for CompressedSNARK * Update dependencies, revert to typenum * Formatting * Update bellperson-nonnative patch * Cleanup * Remove bellperson-nonnative fork * Switch back to fil_pasta_curves * Update forked dependencies * Cleanup * Remove unnecessary patch * Update to lurk-pasta-msm --------- Co-authored-by: porcuquine --- Cargo.toml | 4 +-- src/circuit.rs | 6 ++-- src/commitments.rs | 9 ++++-- src/lib.rs | 9 ++++-- src/nifs.rs | 3 +- src/pasta.rs | 5 ++-- src/poseidon.rs | 9 ++++-- src/r1cs.rs | 15 ++++++---- src/spartan_with_ipa_pc/ipa.rs | 4 ++- src/spartan_with_ipa_pc/mod.rs | 7 +++++ src/spartan_with_ipa_pc/sumcheck.rs | 6 ++-- src/traits/mod.rs | 43 ++++++++++++++++++++++------- src/traits/snark.rs | 10 +++++-- 13 files changed, 93 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index da32cc8..97dc066 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ rand_core = { version = "0.5", default-features = false } rand_chacha = "0.3" itertools = "0.9.0" subtle = "2.4" -pasta_curves = { version = "0.4.0", features = ["repr-c"] } +pasta_curves = { version = "0.5.2", features = ["repr-c", "serde"], package = "fil_pasta_curves" } neptune = { version = "8.1.0", default-features = false } generic-array = "0.14.4" num-bigint = { version = "0.4", features = ["serde", "rand"] } @@ -34,7 +34,7 @@ bitvec = "1.0" byteorder = "1.4.3" [target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] -pasta-msm = "0.1.3" +pasta-msm = { version = "0.1.0", package = "lurk-pasta-msm" } [dev-dependencies] criterion = "0.3.1" diff --git a/src/circuit.rs b/src/circuit.rs index eecf8a3..dfc5c2f 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -28,8 +28,9 @@ use bellperson::{ Circuit, ConstraintSystem, SynthesisError, }; use ff::Field; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct NovaAugmentedCircuitParams { limb_width: usize, n_limbs: usize, @@ -46,7 +47,8 @@ impl NovaAugmentedCircuitParams { } } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct NovaAugmentedCircuitInputs { params: G::Scalar, // Hash(Shape of u2, Gens for u2). Needed for computing the challenge. i: G::Base, diff --git a/src/commitments.rs b/src/commitments.rs index 695e1eb..4415daa 100644 --- a/src/commitments.rs +++ b/src/commitments.rs @@ -10,19 +10,22 @@ use core::{ use ff::Field; use merlin::Transcript; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct CommitGens { gens: Vec, _p: PhantomData, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(bound = "")] pub struct Commitment { pub(crate) comm: G, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(bound = "")] pub struct CompressedCommitment { comm: C, } diff --git a/src/lib.rs b/src/lib.rs index f08aec5..e99a5a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,12 +35,15 @@ use nifs::NIFS; use r1cs::{ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness, }; +use serde::{Deserialize, Serialize}; use traits::{ circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait, }; /// A type that holds public parameters of Nova +#[derive(Serialize, Deserialize)] +#[serde(bound = "")] pub struct PublicParams where G1: Group::Scalar>, @@ -152,7 +155,8 @@ where } /// A SNARK that proves the correct execution of an incremental computation -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct RecursiveSNARK where G1: Group::Scalar>, @@ -497,7 +501,8 @@ where } /// A SNARK that proves the knowledge of a valid `RecursiveSNARK` -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct CompressedSNARK where G1: Group::Scalar>, diff --git a/src/nifs.rs b/src/nifs.rs index b6ac037..9ef010a 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -10,10 +10,11 @@ use super::{ traits::{AbsorbInROTrait, Group, ROTrait}, }; use core::marker::PhantomData; +use serde::{Deserialize, Serialize}; /// A SNARK that holds the proof of a step of an incremental computation #[allow(clippy::upper_case_acronyms)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct NIFS { pub(crate) comm_T: CompressedCommitment, _p: PhantomData, diff --git a/src/pasta.rs b/src/pasta.rs index 7bf0681..97f0319 100644 --- a/src/pasta.rs +++ b/src/pasta.rs @@ -15,6 +15,7 @@ use pasta_curves::{ pallas, vesta, Ep, Eq, }; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; +use serde::{Deserialize, Serialize}; use sha3::Shake256; use std::io::Read; @@ -151,7 +152,7 @@ fn cpu_best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu //////////////////////////////////////Pallas/////////////////////////////////////////////// /// A wrapper for compressed group elements that come from the pallas curve -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct PallasCompressedElementWrapper { repr: [u8; 32], } @@ -266,7 +267,7 @@ impl CompressedGroup for PallasCompressedElementWrapper { //////////////////////////////////////Vesta//////////////////////////////////////////////// /// A wrapper for compressed group elements that come from the vesta curve -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct VestaCompressedElementWrapper { repr: [u8; 32], } diff --git a/src/poseidon.rs b/src/poseidon.rs index 167910c..e93bae9 100644 --- a/src/poseidon.rs +++ b/src/poseidon.rs @@ -20,9 +20,10 @@ use neptune::{ }, Strength, }; +use serde::{Deserialize, Serialize}; /// All Poseidon Constants that are used in Nova -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] pub struct PoseidonConstantsCircuit(PoseidonConstants); impl ROConstantsTrait for PoseidonConstantsCircuit @@ -37,6 +38,7 @@ where } /// A Poseidon-based RO to use outside circuits +#[derive(Serialize, Deserialize)] pub struct PoseidonRO where Base: PrimeField + PrimeFieldBits, @@ -52,7 +54,7 @@ where impl ROTrait for PoseidonRO where - Base: PrimeField + PrimeFieldBits, + Base: PrimeField + PrimeFieldBits + Serialize + for<'de> Deserialize<'de>, Scalar: PrimeField + PrimeFieldBits, { type Constants = PoseidonConstantsCircuit; @@ -107,6 +109,7 @@ where } /// A Poseidon-based RO gadget to use inside the verifier circuit. +#[derive(Serialize, Deserialize)] pub struct PoseidonROCircuit where Scalar: PrimeField + PrimeFieldBits, @@ -120,7 +123,7 @@ where impl ROCircuitTrait for PoseidonROCircuit where - Scalar: PrimeField + PrimeFieldBits, + Scalar: PrimeField + PrimeFieldBits + Serialize + for<'de> Deserialize<'de>, { type Constants = PoseidonConstantsCircuit; diff --git a/src/r1cs.rs b/src/r1cs.rs index d190ec6..fea5b9f 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -18,13 +18,14 @@ use serde::{Deserialize, Serialize}; use sha3::{Digest, Sha3_256}; /// Public parameters for a given R1CS -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] +#[serde(bound = "")] pub struct R1CSGens { pub(crate) gens: CommitGens, } /// A type that holds the shape of the R1CS matrices -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct R1CSShape { pub(crate) num_cons: usize, pub(crate) num_vars: usize, @@ -36,27 +37,29 @@ pub struct R1CSShape { } /// A type that holds a witness for a given R1CS instance -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct R1CSWitness { W: Vec, } /// A type that holds an R1CS instance -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(bound = "")] pub struct R1CSInstance { pub(crate) comm_W: Commitment, pub(crate) X: Vec, } /// A type that holds a witness for a given Relaxed R1CS instance -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct RelaxedR1CSWitness { pub(crate) W: Vec, pub(crate) E: Vec, } /// A type that holds a Relaxed R1CS instance -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(bound = "")] pub struct RelaxedR1CSInstance { pub(crate) comm_W: Commitment, pub(crate) comm_E: Commitment, diff --git a/src/spartan_with_ipa_pc/ipa.rs b/src/spartan_with_ipa_pc/ipa.rs index 3b8f631..3452027 100644 --- a/src/spartan_with_ipa_pc/ipa.rs +++ b/src/spartan_with_ipa_pc/ipa.rs @@ -6,6 +6,7 @@ use core::{cmp::max, iter}; use ff::Field; use merlin::Transcript; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use std::marker::PhantomData; pub fn inner_product(a: &[T], b: &[T]) -> T @@ -66,6 +67,7 @@ impl InnerProductWitness { } /// A non-interactive folding scheme (NIFS) for inner product relations +#[derive(Serialize, Deserialize)] pub struct NIFSForInnerProduct { cross_term: G::Scalar, } @@ -181,7 +183,7 @@ impl NIFSForInnerProduct { } /// An inner product argument -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct InnerProductArgument { L_vec: Vec>, R_vec: Vec>, diff --git a/src/spartan_with_ipa_pc/mod.rs b/src/spartan_with_ipa_pc/mod.rs index 7b9aefb..7f68340 100644 --- a/src/spartan_with_ipa_pc/mod.rs +++ b/src/spartan_with_ipa_pc/mod.rs @@ -20,9 +20,12 @@ use itertools::concat; use merlin::Transcript; use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial}; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use sumcheck::SumcheckProof; /// A type that represents the prover's key +#[derive(Serialize, Deserialize)] +#[serde(bound = "")] pub struct ProverKey { gens_r1cs: R1CSGens, gens_ipa: CommitGens, @@ -40,6 +43,8 @@ impl ProverKeyTrait for ProverKey { } /// A type that represents the verifier's key +#[derive(Serialize, Deserialize)] +#[serde(bound = "")] pub struct VerifierKey { gens_r1cs: R1CSGens, gens_ipa: CommitGens, @@ -59,6 +64,8 @@ impl VerifierKeyTrait for VerifierKey { /// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// The proof is produced using Spartan's combination of the sum-check and /// the commitment to a vector viewed as a polynomial commitment +#[derive(Serialize, Deserialize)] +#[serde(bound = "")] pub struct RelaxedR1CSSNARK { sc_proof_outer: SumcheckProof, claims_outer: (G::Scalar, G::Scalar, G::Scalar), diff --git a/src/spartan_with_ipa_pc/sumcheck.rs b/src/spartan_with_ipa_pc/sumcheck.rs index 3501987..dad4bcb 100644 --- a/src/spartan_with_ipa_pc/sumcheck.rs +++ b/src/spartan_with_ipa_pc/sumcheck.rs @@ -7,8 +7,10 @@ use core::marker::PhantomData; use ff::Field; use merlin::Transcript; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct SumcheckProof { compressed_polys: Vec>, } @@ -226,7 +228,7 @@ pub struct UniPoly { // ax^2 + bx + c stored as vec![a,c] // ax^3 + bx^2 + cx + d stored as vec![a,c,d] -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct CompressedUniPoly { coeffs_except_linear_term: Vec, _p: PhantomData, diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 1c81563..516bd1e 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -10,6 +10,7 @@ use core::{ use ff::{PrimeField, PrimeFieldBits}; use merlin::Transcript; use num_bigint::BigInt; +use serde::{Deserialize, Serialize}; /// Represents an element of a group /// This is currently tailored for an elliptic curve group @@ -25,25 +26,35 @@ pub trait Group: + ScalarMulOwned<::Scalar> + Send + Sync + + Serialize + + for<'de> Deserialize<'de> { /// A type representing an element of the base field of the group - type Base: PrimeField + PrimeFieldBits; + type Base: PrimeField + PrimeFieldBits + Serialize + for<'de> Deserialize<'de>; /// A type representing an element of the scalar field of the group - type Scalar: PrimeField + PrimeFieldBits + ChallengeTrait + Send + Sync; + type Scalar: PrimeField + + PrimeFieldBits + + ChallengeTrait + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de>; /// A type representing the compressed version of the group element - type CompressedGroupElement: CompressedGroup; + type CompressedGroupElement: CompressedGroup + + Serialize + + for<'de> Deserialize<'de>; /// A type representing preprocessed group element - type PreprocessedGroupElement: Clone + Send + Sync; + type PreprocessedGroupElement: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; /// A type that represents a hash function that consumes elements /// from the base field and squeezes out elements of the scalar field - type RO: ROTrait; + type RO: ROTrait + Serialize + for<'de> Deserialize<'de>; /// An alternate implementation of Self::RO in the circuit model - type ROCircuit: ROCircuitTrait; + type ROCircuit: ROCircuitTrait + Serialize + for<'de> Deserialize<'de>; /// A method to compute a multiexponentation fn vartime_multiscalar_mul( @@ -74,9 +85,11 @@ pub trait Group: } /// Represents a compressed version of a group element -pub trait CompressedGroup: Clone + Copy + Debug + Eq + Sized + Send + Sync + 'static { +pub trait CompressedGroup: + Clone + Copy + Debug + Eq + Sized + Send + Sync + Serialize + for<'de> Deserialize<'de> + 'static +{ /// A type that holds the decompressed version of the compressed group element - type GroupElement: Group; + type GroupElement: Group + Serialize + for<'de> Deserialize<'de>; /// Decompresses the compressed group element fn decompress(&self) -> Option; @@ -106,7 +119,12 @@ pub trait ChallengeTrait { /// A helper trait that defines the behavior of a hash function that we use as an RO pub trait ROTrait { /// A type representing constants/parameters associated with the hash function - type Constants: ROConstantsTrait + Clone + Send + Sync; + type Constants: ROConstantsTrait + + Clone + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de>; /// Initializes the hash function fn new(constants: Self::Constants, num_absorbs: usize) -> Self; @@ -121,7 +139,12 @@ pub trait ROTrait { /// A helper trait that defines the behavior of a hash function that we use as an RO in the circuit model pub trait ROCircuitTrait { /// A type representing constants/parameters associated with the hash function - type Constants: ROConstantsTrait + Clone + Send + Sync; + type Constants: ROConstantsTrait + + Clone + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de>; /// Initializes the hash function fn new(constants: Self::Constants, num_absorbs: usize) -> Self; diff --git a/src/traits/snark.rs b/src/traits/snark.rs index 283e622..ff5d7c7 100644 --- a/src/traits/snark.rs +++ b/src/traits/snark.rs @@ -5,6 +5,8 @@ use crate::{ traits::Group, }; +use serde::{Deserialize, Serialize}; + /// A trait that defines the behavior of a zkSNARK's prover key pub trait ProverKeyTrait: Send + Sync { /// Produces a new prover's key @@ -18,12 +20,14 @@ pub trait VerifierKeyTrait: Send + Sync { } /// A trait that defines the behavior of a zkSNARK -pub trait RelaxedR1CSSNARKTrait: Sized + Send + Sync { +pub trait RelaxedR1CSSNARKTrait: + Sized + Send + Sync + Serialize + for<'de> Deserialize<'de> +{ /// A type that represents the prover's key - type ProverKey: ProverKeyTrait; + type ProverKey: ProverKeyTrait + Serialize + for<'de> Deserialize<'de>; /// A type that represents the verifier's key - type VerifierKey: VerifierKeyTrait; + type VerifierKey: VerifierKeyTrait + Serialize + for<'de> Deserialize<'de>; /// Produces a prover key fn prover_key(gens: &R1CSGens, S: &R1CSShape) -> Self::ProverKey {