traits for a vector commitment engine and a polynomial evaluation engine (#136) make spartan generic over the evaluation engine update version disable Wasm CI check
jump to
@@ -17,12 +17,8 @@ - name: Install rustfmt Components
run: rustup component add rustfmt - name: Install clippy run: rustup component add clippy - - name: Install Wasm target - run: rustup target add wasm32-unknown-unknown - name: Build run: cargo build --verbose - - name: Wasm build - run: cargo build --target wasm32-unknown-unknown - name: Build examples run: cargo build --examples --verbose - name: Run tests
@@ -1,6 +1,6 @@
[package] name = "nova-snark" -version = "0.12.0" +version = "0.13.0" authors = ["Srinath Setty <srinath@microsoft.com>"] edition = "2021" description = "Recursive zkSNARKs without trusted setup"@@ -13,7 +13,7 @@
[dependencies] bellperson = { version = "0.24", default-features = false } ff = { version = "0.12.0", features = ["derive"] } -merlin = "2.0.0" +merlin = "3.0.0" digest = "0.8.1" sha3 = "0.8.2" rayon = "1.3.0"
@@ -15,8 +15,10 @@ use std::time::Duration;
type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; -type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>; -type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>; +type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>; +type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>; +type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>; +type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>; type C1 = NonTrivialTestCircuit<<G1 as Group>::Scalar>; type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;
@@ -257,8 +257,11 @@
// produce a compressed SNARK println!("Generating a CompressedSNARK using Spartan with IPA-PC..."); let start = Instant::now(); - type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>; - type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>; + type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>; + type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>; + type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>; + type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>; + let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark); println!( "CompressedSNARK::prove: {:?}, took {:?}",
@@ -182,7 +182,6 @@ cs: &mut CS,
bits: Option<Vec<bool>>, ) -> Result<Vec<AllocatedBit>, SynthesisError> { (0..F::NUM_BITS) - .into_iter() .map(|i| { AllocatedBit::alloc( cs.namespace(|| format!("bit {i}")),
@@ -6,8 +6,7 @@ //! of the running instances. Each of these hashes is
//! H(params = H(shape, gens), i, z0, zi, U). Each circuit folds the last invocation of //! the other into the running instance -use super::{ - commitments::Commitment, +use crate::{ constants::{NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}, gadgets::{ ecc::AllocatedPoint,@@ -17,7 +16,10 @@ alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num,
}, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{circuit::StepCircuit, Group, ROCircuitTrait, ROConstantsCircuit}, + traits::{ + circuit::StepCircuit, commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit, + }, + Commitment, }; use bellperson::{ gadgets::{@@ -59,10 +61,7 @@ u: Option<R1CSInstance<G>>,
T: Option<Commitment<G>>, } -impl<G> NovaAugmentedCircuitInputs<G> -where - G: Group, -{ +impl<G: Group> NovaAugmentedCircuitInputs<G> { /// Create new inputs/witness for the verification circuit #[allow(clippy::too_many_arguments)] pub fn new(@@ -88,22 +87,14 @@ }
/// The augmented circuit F' in Nova that includes a step circuit F /// and the circuit for the verifier in Nova's non-interactive folding scheme -pub struct NovaAugmentedCircuit<G, SC> -where - G: Group, - SC: StepCircuit<G::Base>, -{ +pub struct NovaAugmentedCircuit<G: Group, SC: StepCircuit<G::Base>> { params: NovaAugmentedCircuitParams, ro_consts: ROConstantsCircuit<G>, inputs: Option<NovaAugmentedCircuitInputs<G>>, step_circuit: SC, // The function that is applied for each step } -impl<G, SC> NovaAugmentedCircuit<G, SC> -where - G: Group, - SC: StepCircuit<G::Base>, -{ +impl<G: Group, SC: StepCircuit<G::Base>> NovaAugmentedCircuit<G, SC> { /// Create a new verification circuit for the input relaxed r1cs instances pub fn new( params: NovaAugmentedCircuitParams,@@ -186,10 +177,7 @@ // Allocate T
let T = AllocatedPoint::alloc( cs.namespace(|| "allocate T"), self.inputs.get().map_or(None, |inputs| { - inputs - .T - .get() - .map_or(None, |T| Some(T.comm.to_coordinates())) + inputs.T.get().map_or(None, |T| Some(T.to_coordinates())) }), )?;@@ -274,10 +262,8 @@ Ok((U_fold, check_pass))
} } -impl<G, SC> Circuit<<G as Group>::Base> for NovaAugmentedCircuit<G, SC> -where - G: Group, - SC: StepCircuit<G::Base>, +impl<G: Group, SC: StepCircuit<G::Base>> Circuit<<G as Group>::Base> + for NovaAugmentedCircuit<G, SC> { fn synthesize<CS: ConstraintSystem<<G as Group>::Base>>( self,@@ -388,10 +374,11 @@ use super::*;
use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment}; type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; + use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use crate::{ bellperson::r1cs::{NovaShape, NovaWitness}, - poseidon::PoseidonConstantsCircuit, + provider::poseidon::PoseidonConstantsCircuit, traits::{circuit::TrivialTestCircuit, ROConstantsTrait}, };
@@ -1,6 +1,12 @@
-use super::{ +//! This module provides an implementation of a commitment engine +use crate::{ errors::NovaError, - traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait}, + traits::{ + commitment::{ + CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait, CompressedCommitmentTrait, + }, + AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, ROTrait, + }, }; use core::{ fmt::Debug,@@ -12,27 +18,33 @@ use merlin::Transcript;
use rayon::prelude::*; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CommitGens<G: Group> { +/// A type that holds commitment generators +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CommitmentGens<G: Group> { gens: Vec<G::PreprocessedGroupElement>, _p: PhantomData<G>, } +/// A type that holds a commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct Commitment<G: Group> { pub(crate) comm: G, } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +/// A type that holds a compressed commitment +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct CompressedCommitment<C: CompressedGroup> { comm: C, } -impl<G: Group> CommitGens<G> { - pub fn new(label: &'static [u8], n: usize) -> Self { - CommitGens { +impl<G: Group> CommitmentGensTrait<G> for CommitmentGens<G> { + type Commitment = Commitment<G>; + type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>; + + fn new(label: &'static [u8], n: usize) -> Self { + CommitmentGens { gens: G::from_label(label, n.next_power_of_two()), _p: Default::default(), }@@ -42,76 +54,83 @@ fn len(&self) -> usize {
self.gens.len() } - pub fn split_at(&self, n: usize) -> (CommitGens<G>, CommitGens<G>) { + fn commit(&self, v: &[G::Scalar]) -> Self::Commitment { + assert!(self.gens.len() >= v.len()); + Commitment { + comm: G::vartime_multiscalar_mul(v, &self.gens[..v.len()]), + } + } + + fn split_at(&self, n: usize) -> (CommitmentGens<G>, CommitmentGens<G>) { ( - CommitGens { + CommitmentGens { gens: self.gens[0..n].to_vec(), _p: Default::default(), }, - CommitGens { + CommitmentGens { gens: self.gens[n..].to_vec(), _p: Default::default(), }, ) } - pub fn combine(&self, other: &CommitGens<G>) -> CommitGens<G> { + fn combine(&self, other: &CommitmentGens<G>) -> CommitmentGens<G> { let gens = { let mut c = self.gens.clone(); c.extend(other.gens.clone()); c }; - CommitGens { + CommitmentGens { gens, _p: Default::default(), } } // combines the left and right halves of `self` using `w1` and `w2` as the weights - pub fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitGens<G> { + fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitmentGens<G> { let w = vec![*w1, *w2]; let (L, R) = self.split_at(self.len() / 2); let gens = (0..self.len() / 2) .into_par_iter() .map(|i| { - let gens = CommitGens::<G> { + let gens = CommitmentGens::<G> { gens: [L.gens[i].clone(), R.gens[i].clone()].to_vec(), _p: Default::default(), }; - w.commit(&gens).comm.preprocessed() + gens.commit(&w).comm.preprocessed() }) .collect(); - CommitGens { + CommitmentGens { gens, _p: Default::default(), } } /// Scales each element in `self` by `r` - pub fn scale(&self, r: &G::Scalar) -> Self { + fn scale(&self, r: &G::Scalar) -> Self { let gens_scaled = self .gens .clone() .into_par_iter() .map(|g| { - let gens = CommitGens::<G> { + let gens = CommitmentGens::<G> { gens: vec![g], _p: Default::default(), }; - [*r].commit(&gens).comm.preprocessed() + gens.commit(&[*r]).comm.preprocessed() }) .collect(); - CommitGens { + CommitmentGens { gens: gens_scaled, _p: Default::default(), } } /// reinterprets a vector of commitments as a set of generators - pub fn reinterpret_commitments_as_gens( + fn reinterpret_commitments_as_gens( c: &[CompressedCommitment<G::CompressedGroupElement>], ) -> Result<Self, NovaError> { let d = (0..c.len())@@ -122,19 +141,25 @@ let gens = (0..d.len())
.into_par_iter() .map(|i| d[i].comm.preprocessed()) .collect(); - Ok(CommitGens { + Ok(CommitmentGens { gens, _p: Default::default(), }) } } -impl<G: Group> Commitment<G> { - pub fn compress(&self) -> CompressedCommitment<G::CompressedGroupElement> { +impl<G: Group> CommitmentTrait<G> for Commitment<G> { + type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>; + + fn compress(&self) -> CompressedCommitment<G::CompressedGroupElement> { CompressedCommitment { comm: self.comm.compress(), } } + + fn to_coordinates(&self) -> (G::Base, G::Base, bool) { + self.comm.to_coordinates() + } } impl<G: Group> Default for Commitment<G> {@@ -143,8 +168,10 @@ Commitment { comm: G::zero() }
} } -impl<C: CompressedGroup> CompressedCommitment<C> { - pub fn decompress(&self) -> Result<Commitment<C::GroupElement>, NovaError> { +impl<C: CompressedGroup> CompressedCommitmentTrait<C> for CompressedCommitment<C> { + type Commitment = Commitment<C::GroupElement>; + + fn decompress(&self) -> Result<Self::Commitment, NovaError> { let comm = self.comm.decompress(); if comm.is_none() { return Err(NovaError::DecompressionError);@@ -155,19 +182,6 @@ })
} } -pub trait CommitTrait<G: Group> { - fn commit(&self, gens: &CommitGens<G>) -> Commitment<G>; -} - -impl<G: Group> CommitTrait<G> for [G::Scalar] { - fn commit(&self, gens: &CommitGens<G>) -> Commitment<G> { - assert!(gens.gens.len() >= self.len()); - Commitment { - comm: G::vartime_multiscalar_mul(self, &gens.gens[..self.len()]), - } - } -} - impl<G: Group> AppendToTranscriptTrait for Commitment<G> { fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { transcript.append_message(label, self.comm.compress().as_bytes());@@ -193,8 +207,8 @@ transcript.append_message(label, self.comm.as_bytes());
} } -impl<'b, G: Group> MulAssign<&'b G::Scalar> for Commitment<G> { - fn mul_assign(&mut self, scalar: &'b G::Scalar) { +impl<G: Group> MulAssign<G::Scalar> for Commitment<G> { + fn mul_assign(&mut self, scalar: G::Scalar) { let result = (self as &Commitment<G>).comm * scalar; *self = Commitment { comm: result }; }@@ -272,3 +286,19 @@ }
define_add_assign_variants!(G = Group, LHS = Commitment<G>, RHS = Commitment<G>); define_add_variants!(G = Group, LHS = Commitment<G>, RHS = Commitment<G>, Output = Commitment<G>); + +/// Provides a commitment engine +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CommitmentEngine<G: Group> { + _p: PhantomData<G>, +} + +impl<G: Group> CommitmentEngineTrait<G> for CommitmentEngine<G> { + type CommitmentGens = CommitmentGens<G>; + type Commitment = Commitment<G>; + type CompressedCommitment = CompressedCommitment<G::CompressedGroupElement>; + + fn commit(gens: &Self::CommitmentGens, v: &[G::Scalar]) -> Self::Commitment { + gens.commit(v) + } +}
@@ -13,7 +13,7 @@ conditionally_select_bignat, le_bits_to_num,
}, }, r1cs::{R1CSInstance, RelaxedR1CSInstance}, - traits::{Group, ROCircuitTrait, ROConstantsCircuit}, + traits::{commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit}, }; use bellperson::{ gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},@@ -23,19 +23,13 @@ use ff::Field;
/// An Allocated R1CS Instance #[derive(Clone)] -pub struct AllocatedR1CSInstance<G> -where - G: Group, -{ +pub struct AllocatedR1CSInstance<G: Group> { pub(crate) W: AllocatedPoint<G>, pub(crate) X0: AllocatedNum<G::Base>, pub(crate) X1: AllocatedNum<G::Base>, } -impl<G> AllocatedR1CSInstance<G> -where - G: Group, -{ +impl<G: Group> AllocatedR1CSInstance<G> { /// Takes the r1cs instance and creates a new allocated r1cs instance pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>( mut cs: CS,@@ -44,8 +38,7 @@ ) -> Result<Self, SynthesisError> {
// Check that the incoming instance has exactly 2 io let W = AllocatedPoint::alloc( cs.namespace(|| "allocate W"), - u.get() - .map_or(None, |u| Some(u.comm_W.comm.to_coordinates())), + u.get().map_or(None, |u| Some(u.comm_W.to_coordinates())), )?; let X0 = alloc_scalar_as_base::<G, _>(@@ -71,10 +64,7 @@ }
} /// An Allocated Relaxed R1CS Instance -pub struct AllocatedRelaxedR1CSInstance<G> -where - G: Group, -{ +pub struct AllocatedRelaxedR1CSInstance<G: Group> { pub(crate) W: AllocatedPoint<G>, pub(crate) E: AllocatedPoint<G>, pub(crate) u: AllocatedNum<G::Base>,@@ -82,10 +72,7 @@ pub(crate) X0: BigNat<G::Base>,
pub(crate) X1: BigNat<G::Base>, } -impl<G> AllocatedRelaxedR1CSInstance<G> -where - G: Group, -{ +impl<G: Group> AllocatedRelaxedR1CSInstance<G> { /// Allocates the given RelaxedR1CSInstance as a witness of the circuit pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>( mut cs: CS,@@ -97,14 +84,14 @@ let W = AllocatedPoint::alloc(
cs.namespace(|| "allocate W"), inst .get() - .map_or(None, |inst| Some(inst.comm_W.comm.to_coordinates())), + .map_or(None, |inst| Some(inst.comm_W.to_coordinates())), )?; let E = AllocatedPoint::alloc( cs.namespace(|| "allocate E"), inst .get() - .map_or(None, |inst| Some(inst.comm_E.comm.to_coordinates())), + .map_or(None, |inst| Some(inst.comm_E.to_coordinates())), )?; // u << |G::Base| despite the fact that u is a scalar.
@@ -6,17 +6,15 @@
// private modules mod bellperson; mod circuit; -mod commitments; mod constants; mod nifs; -mod poseidon; mod r1cs; // public modules pub mod errors; pub mod gadgets; -pub mod pasta; -pub mod spartan_with_ipa_pc; +pub mod provider; +pub mod spartan; pub mod traits; use crate::bellperson::{@@ -37,8 +35,10 @@ R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
}; use serde::{Deserialize, Serialize}; use traits::{ - circuit::StepCircuit, snark::RelaxedR1CSSNARKTrait, AbsorbInROTrait, Group, ROConstants, - ROConstantsCircuit, ROConstantsTrait, ROTrait, + circuit::StepCircuit, + commitment::{CommitmentEngineTrait, CompressedCommitmentTrait}, + snark::RelaxedR1CSSNARKTrait, + AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait, }; /// A type that holds public parameters of Nova@@ -717,13 +717,20 @@ Ok((self.zn_primary.clone(), self.zn_secondary.clone()))
} } +type CommitmentGens<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentGens; +type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment; +type CompressedCommitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::CompressedCommitment; +type CE<G> = <G as Group>::CE; + #[cfg(test)] mod tests { use super::*; type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; - type S1 = spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>; - type S2 = spartan_with_ipa_pc::RelaxedR1CSSNARK<G2>; + type EE1 = provider::ipa_pc::EvaluationEngine<G1>; + type EE2 = provider::ipa_pc::EvaluationEngine<G2>; + type S1 = spartan::RelaxedR1CSSNARK<G1, EE1>; + type S2 = spartan::RelaxedR1CSSNARK<G2, EE2>; use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; use core::marker::PhantomData; use ff::PrimeField;
@@ -2,12 +2,15 @@ //! This module implements a non-interactive folding scheme
#![allow(non_snake_case)] #![allow(clippy::type_complexity)] -use super::{ - commitments::CompressedCommitment, +use crate::{ constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO}, errors::NovaError, r1cs::{R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}, - traits::{AbsorbInROTrait, Group, ROTrait}, + traits::{ + commitment::{CommitmentTrait, CompressedCommitmentTrait}, + AbsorbInROTrait, Group, ROTrait, + }, + CompressedCommitment, }; use core::marker::PhantomData; use serde::{Deserialize, Serialize};@@ -15,8 +18,9 @@
/// A SNARK that holds the proof of a step of an incremental computation #[allow(clippy::upper_case_acronyms)] #[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] pub struct NIFS<G: Group> { - pub(crate) comm_T: CompressedCommitment<G::CompressedGroupElement>, + pub(crate) comm_T: CompressedCommitment<G>, _p: PhantomData<G>, }
@@ -1,6 +1,9 @@
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar. use crate::{ - poseidon::{PoseidonRO, PoseidonROCircuit}, + provider::{ + pedersen::CommitmentEngine, + poseidon::{PoseidonRO, PoseidonROCircuit}, + }, traits::{ChallengeTrait, CompressedGroup, Group}, }; use digest::{ExtendableOutput, Input};@@ -61,6 +64,7 @@ type CompressedGroupElement = $name_compressed;
type PreprocessedGroupElement = $name::Affine; type RO = PoseidonRO<Self::Base, Self::Scalar>; type ROCircuit = PoseidonROCircuit<Self::Base>; + type CE = CommitmentEngine<Self>; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] fn vartime_multiscalar_mul(
@@ -1,5 +1,5 @@
//! Poseidon Constants and Poseidon-based RO used in Nova -use super::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait}; +use crate::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait}; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean},
@@ -0,0 +1,10 @@
+//! This module implements Nova's traits using the following configuration: +//! `CommitmentEngine` with Pedersen's commitments +//! `Group` with pasta curves +//! `RO` traits with Poseidon +//! `EvaluationEngine` with an IPA-based polynomial evaluation argument + +pub mod ipa_pc; +pub mod pasta; +pub mod pedersen; +pub mod poseidon;
@@ -1,12 +1,17 @@
//! This module defines R1CS related types and a folding scheme for Relaxed R1CS #![allow(clippy::type_complexity)] -use super::gadgets::nonnative::{bignat::nat_to_limbs, util::f_to_nat}; -use super::{ - commitments::{CommitGens, CommitTrait, Commitment}, +use crate::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS}, errors::NovaError, - gadgets::utils::scalar_as_base, - traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait}, + gadgets::{ + nonnative::{bignat::nat_to_limbs, util::f_to_nat}, + utils::scalar_as_base, + }, + traits::{ + commitment::{CommitmentEngineTrait, CommitmentGensTrait}, + AbsorbInROTrait, AppendToTranscriptTrait, Group, ROTrait, + }, + Commitment, CommitmentGens, CE, }; use core::cmp::max; use ff::Field;@@ -21,7 +26,7 @@ /// Public parameters for a given R1CS
#[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] pub struct R1CSGens<G: Group> { - pub(crate) gens: CommitGens<G>, + pub(crate) gens: CommitmentGens<G>, } /// A type that holds the shape of the R1CS matrices@@ -71,7 +76,7 @@ impl<G: Group> R1CSGens<G> {
/// Samples public parameters for the specified number of constraints and variables in an R1CS pub fn new(num_cons: usize, num_vars: usize) -> R1CSGens<G> { R1CSGens { - gens: CommitGens::new(b"gens", max(num_vars, num_cons)), + gens: CommitmentGens::<G>::new(b"gens", max(num_vars, num_cons)), } } }@@ -202,7 +207,10 @@ };
// verify if comm_E and comm_W are commitments to E and W let res_comm: bool = { - let (comm_W, comm_E) = rayon::join(|| W.W.commit(&gens.gens), || W.E.commit(&gens.gens)); + let (comm_W, comm_E) = rayon::join( + || CE::<G>::commit(&gens.gens, &W.W), + || CE::<G>::commit(&gens.gens, &W.E), + ); U.comm_W == comm_W && U.comm_E == comm_E };@@ -239,7 +247,7 @@ res == 0
}; // verify if comm_W is a commitment to W - let res_comm: bool = U.comm_W == W.W.commit(&gens.gens); + let res_comm: bool = U.comm_W == gens.gens.commit(&W.W); if res_eq && res_comm { Ok(())@@ -293,7 +301,7 @@ .zip(&u_2_cdot_CZ_1)
.map(|(((a, b), c), d)| *a + *b - *c - *d) .collect::<Vec<G::Scalar>>(); - let comm_T = T.commit(&gens.gens); + let comm_T = gens.gens.commit(&T); Ok((T, comm_T)) }@@ -458,7 +466,7 @@ }
/// Commits to the witness using the supplied generators pub fn commit(&self, gens: &R1CSGens<G>) -> Commitment<G> { - self.W.commit(&gens.gens) + gens.gens.commit(&self.W) } }@@ -515,7 +523,10 @@ }
/// Commits to the witness using the supplied generators pub fn commit(&self, gens: &R1CSGens<G>) -> (Commitment<G>, Commitment<G>) { - (self.W.commit(&gens.gens), self.E.commit(&gens.gens)) + ( + CE::<G>::commit(&gens.gens, &self.W), + CE::<G>::commit(&gens.gens, &self.E), + ) } /// Folds an incoming R1CSWitness into the current one@@ -566,7 +577,7 @@
impl<G: Group> RelaxedR1CSInstance<G> { /// Produces a default RelaxedR1CSInstance given R1CSGens and R1CSShape pub fn default(_gens: &R1CSGens<G>, S: &R1CSShape<G>) -> RelaxedR1CSInstance<G> { - let (comm_W, comm_E) = (Commitment::default(), Commitment::default()); + let (comm_W, comm_E) = (Commitment::<G>::default(), Commitment::<G>::default()); RelaxedR1CSInstance { comm_W, comm_E,@@ -605,7 +616,7 @@ .par_iter()
.zip(X2) .map(|(a, b)| *a + *r * *b) .collect::<Vec<G::Scalar>>(); - let comm_W = comm_W_1 + comm_W_2 * r; + let comm_W = *comm_W_1 + *comm_W_2 * *r; let comm_E = *comm_E_1 + *comm_T * *r; let u = *u1 + *r;
@@ -1,7 +1,15 @@
+//! This module implements `EvaluationEngine` using an IPA-based polynomial commitment scheme #![allow(clippy::too_many_arguments)] -use crate::commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment}; -use crate::errors::NovaError; -use crate::traits::{AppendToTranscriptTrait, ChallengeTrait, Group}; +use crate::{ + errors::NovaError, + spartan::polynomial::EqPolynomial, + traits::{ + commitment::{CommitmentEngineTrait, CommitmentGensTrait, CommitmentTrait}, + evaluation::EvaluationEngineTrait, + AppendToTranscriptTrait, ChallengeTrait, Group, + }, + Commitment, CommitmentGens, CompressedCommitment, CE, +}; use core::{cmp::max, iter}; use ff::Field; use merlin::Transcript;@@ -9,7 +17,131 @@ use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use std::marker::PhantomData; -pub fn inner_product<T>(a: &[T], b: &[T]) -> T +/// Provides an implementation of generators for proving evaluations +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct EvaluationGens<G: Group> { + gens_v: CommitmentGens<G>, + gens_s: CommitmentGens<G>, +} + +/// Provides an implementation of a polynomial evaluation argument +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct EvaluationArgument<G: Group> { + nifs: Vec<NIFSForInnerProduct<G>>, + ipa: InnerProductArgument<G>, +} + +/// Provides an implementation of a polynomial evaluation engine using IPA +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EvaluationEngine<G: Group> { + _p: PhantomData<G>, +} + +impl<G: Group> EvaluationEngineTrait<G> for EvaluationEngine<G> { + type CE = G::CE; + type EvaluationGens = EvaluationGens<G>; + type EvaluationArgument = EvaluationArgument<G>; + + fn setup(gens: &<Self::CE as CommitmentEngineTrait<G>>::CommitmentGens) -> Self::EvaluationGens { + EvaluationGens { + gens_v: gens.clone(), + gens_s: CommitmentGens::<G>::new(b"ipa", 1), + } + } + + fn prove_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comms: &[Commitment<G>], + polys: &[Vec<G::Scalar>], + points: &[Vec<G::Scalar>], + evals: &[G::Scalar], + ) -> Result<Self::EvaluationArgument, NovaError> { + // sanity checks (these should never fail) + assert!(polys.len() >= 2); + assert_eq!(comms.len(), polys.len()); + assert_eq!(comms.len(), points.len()); + assert_eq!(comms.len(), evals.len()); + + let mut r_U = InnerProductInstance::new( + &comms[0], + &EqPolynomial::new(points[0].clone()).evals(), + &evals[0], + ); + let mut r_W = InnerProductWitness::new(&polys[0]); + let mut nifs = Vec::new(); + + for i in 1..polys.len() { + let (n, u, w) = NIFSForInnerProduct::prove( + &r_U, + &r_W, + &InnerProductInstance::new( + &comms[i], + &EqPolynomial::new(points[i].clone()).evals(), + &evals[i], + ), + &InnerProductWitness::new(&polys[i]), + transcript, + ); + nifs.push(n); + r_U = u; + r_W = w; + } + + let ipa = InnerProductArgument::prove(&gens.gens_v, &gens.gens_s, &r_U, &r_W, transcript)?; + + Ok(EvaluationArgument { nifs, ipa }) + } + + /// A method to verify purported evaluations of a batch of polynomials + fn verify_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comms: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment], + points: &[Vec<G::Scalar>], + evals: &[G::Scalar], + arg: &Self::EvaluationArgument, + ) -> Result<(), NovaError> { + // sanity checks (these should never fail) + assert!(comms.len() >= 2); + assert_eq!(comms.len(), points.len()); + assert_eq!(comms.len(), evals.len()); + + let mut r_U = InnerProductInstance::new( + &comms[0], + &EqPolynomial::new(points[0].clone()).evals(), + &evals[0], + ); + let mut num_vars = points[0].len(); + for i in 1..comms.len() { + let u = arg.nifs[i - 1].verify( + &r_U, + &InnerProductInstance::new( + &comms[i], + &EqPolynomial::new(points[i].clone()).evals(), + &evals[i], + ), + transcript, + ); + r_U = u; + num_vars = max(num_vars, points[i].len()); + } + + arg.ipa.verify( + &gens.gens_v, + &gens.gens_s, + (2_usize).pow(num_vars as u32), + &r_U, + transcript, + )?; + + Ok(()) + } +} + +fn inner_product<T>(a: &[T], b: &[T]) -> T where T: Field + Send + Sync, {@@ -29,7 +161,7 @@ c: G::Scalar,
} impl<G: Group> InnerProductInstance<G> { - pub fn new(comm_a_vec: &Commitment<G>, b_vec: &[G::Scalar], c: &G::Scalar) -> Self { + fn new(comm_a_vec: &Commitment<G>, b_vec: &[G::Scalar], c: &G::Scalar) -> Self { InnerProductInstance { comm_a_vec: *comm_a_vec, b_vec: b_vec.to_vec(),@@ -37,7 +169,7 @@ c: *c,
} } - pub fn pad(&self, n: usize) -> InnerProductInstance<G> { + fn pad(&self, n: usize) -> InnerProductInstance<G> { let mut b_vec = self.b_vec.clone(); b_vec.resize(n, G::Scalar::zero()); InnerProductInstance {@@ -48,18 +180,18 @@ }
} } -pub struct InnerProductWitness<G: Group> { +struct InnerProductWitness<G: Group> { a_vec: Vec<G::Scalar>, } impl<G: Group> InnerProductWitness<G> { - pub fn new(a_vec: &[G::Scalar]) -> Self { + fn new(a_vec: &[G::Scalar]) -> Self { InnerProductWitness { a_vec: a_vec.to_vec(), } } - pub fn pad(&self, n: usize) -> InnerProductWitness<G> { + fn pad(&self, n: usize) -> InnerProductWitness<G> { let mut a_vec = self.a_vec.clone(); a_vec.resize(n, G::Scalar::zero()); InnerProductWitness { a_vec }@@ -67,17 +199,17 @@ }
} /// A non-interactive folding scheme (NIFS) for inner product relations -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct NIFSForInnerProduct<G: Group> { cross_term: G::Scalar, } impl<G: Group> NIFSForInnerProduct<G> { - pub fn protocol_name() -> &'static [u8] { + fn protocol_name() -> &'static [u8] { b"NIFSForInnerProduct" } - pub fn prove( + fn prove( U1: &InnerProductInstance<G>, W1: &InnerProductWitness<G>, U2: &InnerProductInstance<G>,@@ -136,7 +268,7 @@
(NIFSForInnerProduct { cross_term }, U, W) } - pub fn verify( + fn verify( &self, U1: &InnerProductInstance<G>, U2: &InnerProductInstance<G>,@@ -183,10 +315,11 @@ }
} /// An inner product argument -#[derive(Debug, Serialize, Deserialize)] -pub struct InnerProductArgument<G: Group> { - L_vec: Vec<CompressedCommitment<G::CompressedGroupElement>>, - R_vec: Vec<CompressedCommitment<G::CompressedGroupElement>>, +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +struct InnerProductArgument<G: Group> { + L_vec: Vec<CompressedCommitment<G>>, + R_vec: Vec<CompressedCommitment<G>>, a_hat: G::Scalar, _p: PhantomData<G>, }@@ -196,9 +329,9 @@ fn protocol_name() -> &'static [u8] {
b"inner product argument" } - pub fn prove( - gens: &CommitGens<G>, - gens_c: &CommitGens<G>, + fn prove( + gens: &CommitmentGens<G>, + gens_c: &CommitmentGens<G>, U: &InnerProductInstance<G>, W: &InnerProductWitness<G>, transcript: &mut Transcript,@@ -220,15 +353,15 @@
// a closure that executes a step of the recursive inner product argument let prove_inner = |a_vec: &[G::Scalar], b_vec: &[G::Scalar], - gens: &CommitGens<G>, + gens: &CommitmentGens<G>, transcript: &mut Transcript| -> Result< ( - CompressedCommitment<G::CompressedGroupElement>, - CompressedCommitment<G::CompressedGroupElement>, + CompressedCommitment<G>, + CompressedCommitment<G>, Vec<G::Scalar>, Vec<G::Scalar>, - CommitGens<G>, + CommitmentGens<G>, ), NovaError, > {@@ -238,20 +371,24 @@
let c_L = inner_product(&a_vec[0..n / 2], &b_vec[n / 2..n]); let c_R = inner_product(&a_vec[n / 2..n], &b_vec[0..n / 2]); - let L = a_vec[0..n / 2] - .iter() - .chain(iter::once(&c_L)) - .copied() - .collect::<Vec<G::Scalar>>() - .commit(&gens_R.combine(&gens_c)) - .compress(); - let R = a_vec[n / 2..n] - .iter() - .chain(iter::once(&c_R)) - .copied() - .collect::<Vec<G::Scalar>>() - .commit(&gens_L.combine(&gens_c)) - .compress(); + let L = CE::<G>::commit( + &gens_R.combine(&gens_c), + &a_vec[0..n / 2] + .iter() + .chain(iter::once(&c_L)) + .copied() + .collect::<Vec<G::Scalar>>(), + ) + .compress(); + let R = CE::<G>::commit( + &gens_L.combine(&gens_c), + &a_vec[n / 2..n] + .iter() + .chain(iter::once(&c_R)) + .copied() + .collect::<Vec<G::Scalar>>(), + ) + .compress(); L.append_to_transcript(b"L", transcript); R.append_to_transcript(b"R", transcript);@@ -278,8 +415,8 @@ Ok((L, R, a_vec_folded, b_vec_folded, gens_folded))
}; // two vectors to hold the logarithmic number of group elements - let mut L_vec: Vec<CompressedCommitment<G::CompressedGroupElement>> = Vec::new(); - let mut R_vec: Vec<CompressedCommitment<G::CompressedGroupElement>> = Vec::new(); + let mut L_vec: Vec<CompressedCommitment<G>> = Vec::new(); + let mut R_vec: Vec<CompressedCommitment<G>> = Vec::new(); // we create mutable copies of vectors and generators let mut a_vec = W.a_vec.to_vec();@@ -304,10 +441,10 @@ _p: Default::default(),
}) } - pub fn verify( + fn verify( &self, - gens: &CommitGens<G>, - gens_c: &CommitGens<G>, + gens: &CommitmentGens<G>, + gens_c: &CommitmentGens<G>, n: usize, U: &InnerProductInstance<G>, transcript: &mut Transcript,@@ -329,7 +466,7 @@ // sample a random base for commiting to the inner product
let r = G::Scalar::challenge(b"r", transcript); let gens_c = gens_c.scale(&r); - let P = U.comm_a_vec + [U.c].commit(&gens_c); + let P = U.comm_a_vec + CE::<G>::commit(&gens_c, &[U.c]); let batch_invert = |v: &[G::Scalar]| -> Result<Vec<G::Scalar>, NovaError> { let mut products = vec![G::Scalar::zero(); v.len()];@@ -396,29 +533,37 @@ s
}; let gens_hat = { - let c = s.commit(gens).compress(); - CommitGens::reinterpret_commitments_as_gens(&[c])? + let c = CE::<G>::commit(gens, &s).compress(); + CommitmentGens::<G>::reinterpret_commitments_as_gens(&[c])? }; let b_hat = inner_product(&U.b_vec, &s); let P_hat = { let gens_folded = { - let gens_L = CommitGens::reinterpret_commitments_as_gens(&self.L_vec)?; - let gens_R = CommitGens::reinterpret_commitments_as_gens(&self.R_vec)?; - let gens_P = CommitGens::reinterpret_commitments_as_gens(&[P.compress()])?; + let gens_L = CommitmentGens::<G>::reinterpret_commitments_as_gens(&self.L_vec)?; + let gens_R = CommitmentGens::<G>::reinterpret_commitments_as_gens(&self.R_vec)?; + let gens_P = CommitmentGens::<G>::reinterpret_commitments_as_gens(&[P.compress()])?; gens_L.combine(&gens_R).combine(&gens_P) }; - r_square - .iter() - .chain(r_inverse_square.iter()) - .chain(iter::once(&G::Scalar::one())) - .copied() - .collect::<Vec<G::Scalar>>() - .commit(&gens_folded) + + CE::<G>::commit( + &gens_folded, + &r_square + .iter() + .chain(r_inverse_square.iter()) + .chain(iter::once(&G::Scalar::one())) + .copied() + .collect::<Vec<G::Scalar>>(), + ) }; - if P_hat == [self.a_hat, self.a_hat * b_hat].commit(&gens_hat.combine(&gens_c)) { + if P_hat + == CE::<G>::commit( + &gens_hat.combine(&gens_c), + &[self.a_hat, self.a_hat * b_hat], + ) + { Ok(()) } else { Err(NovaError::InvalidIPA)
@@ -1,21 +1,18 @@
-//! This module implements RelaxedR1CSSNARKTrait using a Spartan variant -//! instantiated with an IPA-based polynomial commitment scheme -mod ipa; -mod polynomial; +//! This module implements RelaxedR1CSSNARKTrait using Spartan that is generic +//! over the polynomial commitment and evaluation argument (i.e., a PCS) +pub mod polynomial; mod sumcheck; -use super::{ - commitments::CommitGens, +use crate::{ errors::NovaError, r1cs::{R1CSGens, R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, traits::{ + evaluation::EvaluationEngineTrait, snark::{ProverKeyTrait, RelaxedR1CSSNARKTrait, VerifierKeyTrait}, AppendToTranscriptTrait, ChallengeTrait, Group, }, }; -use core::cmp::max; use ff::Field; -use ipa::{InnerProductArgument, InnerProductInstance, InnerProductWitness, NIFSForInnerProduct}; use itertools::concat; use merlin::Transcript; use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial};@@ -26,17 +23,15 @@
/// A type that represents the prover's key #[derive(Serialize, Deserialize)] #[serde(bound = "")] -pub struct ProverKey<G: Group> { - gens_r1cs: R1CSGens<G>, - gens_ipa: CommitGens<G>, +pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> { + gens: EE::EvaluationGens, S: R1CSShape<G>, } -impl<G: Group> ProverKeyTrait<G> for ProverKey<G> { +impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> ProverKeyTrait<G> for ProverKey<G, EE> { fn new(gens: &R1CSGens<G>, S: &R1CSShape<G>) -> Self { ProverKey { - gens_r1cs: gens.clone(), - gens_ipa: CommitGens::new(b"ipa", 1), + gens: EE::setup(&gens.gens), S: S.clone(), } }@@ -45,17 +40,17 @@
/// A type that represents the verifier's key #[derive(Serialize, Deserialize)] #[serde(bound = "")] -pub struct VerifierKey<G: Group> { - gens_r1cs: R1CSGens<G>, - gens_ipa: CommitGens<G>, +pub struct VerifierKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> { + gens: EE::EvaluationGens, S: R1CSShape<G>, } -impl<G: Group> VerifierKeyTrait<G> for VerifierKey<G> { +impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> VerifierKeyTrait<G> + for VerifierKey<G, EE> +{ fn new(gens: &R1CSGens<G>, S: &R1CSShape<G>) -> Self { VerifierKey { - gens_r1cs: gens.clone(), - gens_ipa: CommitGens::new(b"ipa", 1), + gens: EE::setup(&gens.gens), S: S.clone(), } }@@ -66,19 +61,20 @@ /// 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<G: Group> { +pub struct RelaxedR1CSSNARK<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> { sc_proof_outer: SumcheckProof<G>, claims_outer: (G::Scalar, G::Scalar, G::Scalar), sc_proof_inner: SumcheckProof<G>, eval_E: G::Scalar, eval_W: G::Scalar, - nifs_ip: NIFSForInnerProduct<G>, - ipa: InnerProductArgument<G>, + eval_arg: EE::EvaluationArgument, } -impl<G: Group> RelaxedR1CSSNARKTrait<G> for RelaxedR1CSSNARK<G> { - type ProverKey = ProverKey<G>; - type VerifierKey = VerifierKey<G>; +impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G> + for RelaxedR1CSSNARK<G, EE> +{ + type ProverKey = ProverKey<G, EE>; + type VerifierKey = VerifierKey<G, EE>; /// produces a succinct proof of satisfiability of a RelaxedR1CS instance fn prove(@@ -88,8 +84,6 @@ W: &RelaxedR1CSWitness<G>,
) -> Result<Self, NovaError> { let mut transcript = Transcript::new(b"RelaxedR1CSSNARK"); - debug_assert!(pk.S.is_sat_relaxed(&pk.gens_r1cs, U, W).is_ok()); - // sanity check that R1CSShape has certain size characteristics assert_eq!(pk.S.num_cons.next_power_of_two(), pk.S.num_cons); assert_eq!(pk.S.num_vars.next_power_of_two(), pk.S.num_vars);@@ -230,24 +224,13 @@
let eval_W = MultilinearPolynomial::new(W.W.clone()).evaluate(&r_y[1..]); eval_W.append_to_transcript(b"eval_W", &mut transcript); - let (nifs_ip, r_U, r_W) = NIFSForInnerProduct::prove( - &InnerProductInstance::new(&U.comm_E, &EqPolynomial::new(r_x).evals(), &eval_E), - &InnerProductWitness::new(&W.E), - &InnerProductInstance::new( - &U.comm_W, - &EqPolynomial::new(r_y[1..].to_vec()).evals(), - &eval_W, - ), - &InnerProductWitness::new(&W.W), + let eval_arg = EE::prove_batch( + &pk.gens, &mut transcript, - ); - - let ipa = InnerProductArgument::prove( - &pk.gens_r1cs.gens, - &pk.gens_ipa, - &r_U, - &r_W, - &mut transcript, + &[U.comm_E, U.comm_W], + &[W.E.clone(), W.W.clone()], + &[r_x, r_y[1..].to_vec()], + &[eval_E, eval_W], )?; Ok(RelaxedR1CSSNARK {@@ -256,8 +239,7 @@ claims_outer: (claim_Az, claim_Bz, claim_Cz),
sc_proof_inner, eval_W, eval_E, - nifs_ip, - ipa, + eval_arg, }) }@@ -366,22 +348,13 @@
// verify eval_W and eval_E self.eval_W.append_to_transcript(b"eval_W", &mut transcript); //eval_E is already in the transcript - let r_U = self.nifs_ip.verify( - &InnerProductInstance::new(&U.comm_E, &EqPolynomial::new(r_x).evals(), &self.eval_E), - &InnerProductInstance::new( - &U.comm_W, - &EqPolynomial::new(r_y[1..].to_vec()).evals(), - &self.eval_W, - ), + EE::verify_batch( + &vk.gens, &mut transcript, - ); - - self.ipa.verify( - &vk.gens_r1cs.gens, - &vk.gens_ipa, - max(vk.S.num_vars, vk.S.num_cons), - &r_U, - &mut transcript, + &[U.comm_E, U.comm_W], + &[r_x, r_y[1..].to_vec()], + &[self.eval_E, self.eval_W], + &self.eval_arg, )?; Ok(())
@@ -1,16 +1,19 @@
+//! This module defines basic types related to polynomials use core::ops::Index; use ff::PrimeField; use rayon::prelude::*; -pub struct EqPolynomial<Scalar: PrimeField> { +pub(crate) struct EqPolynomial<Scalar: PrimeField> { r: Vec<Scalar>, } impl<Scalar: PrimeField> EqPolynomial<Scalar> { + /// Creates a new polynomial from its succinct specification pub fn new(r: Vec<Scalar>) -> Self { EqPolynomial { r } } + /// Evaluates the polynomial at the specified point pub fn evaluate(&self, rx: &[Scalar]) -> Scalar { assert_eq!(self.r.len(), rx.len()); (0..rx.len())@@ -43,7 +46,7 @@ }
} #[derive(Debug)] -pub struct MultilinearPolynomial<Scalar: PrimeField> { +pub(crate) struct MultilinearPolynomial<Scalar: PrimeField> { num_vars: usize, // the number of variables in the multilinear polynomial Z: Vec<Scalar>, // evaluations of the polynomial in all the 2^num_vars Boolean inputs }@@ -105,7 +108,7 @@ &(self.Z[_index])
} } -pub struct SparsePolynomial<Scalar: PrimeField> { +pub(crate) struct SparsePolynomial<Scalar: PrimeField> { num_vars: usize, Z: Vec<(usize, Scalar)>, }
@@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] -pub struct SumcheckProof<G: Group> { +pub(crate) struct SumcheckProof<G: Group> { compressed_polys: Vec<CompressedUniPoly<G>>, }
@@ -0,0 +1,149 @@
+//! This module defines a collection of traits that define the behavior of a commitment engine +//! We require the commitment engine to provide a commitment to vectors with a single group element +use crate::{ + errors::NovaError, + traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group}, +}; +use core::{ + fmt::Debug, + ops::{Add, AddAssign, Mul, MulAssign}, +}; +use serde::{Deserialize, Serialize}; + +/// This trait defines the behavior of commitment key +#[allow(clippy::len_without_is_empty)] +pub trait CommitmentGensTrait<G: Group>: + Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de> +{ + /// Holds the type of the commitment that can be produced + type Commitment; + + /// Holds the type of the compressed commitment + type CompressedCommitment; + + /// Samples a new commitment key of a specified size + fn new(label: &'static [u8], n: usize) -> Self; + + /// Returns the vector length that can be committed + fn len(&self) -> usize; + + /// Commits to a vector using the commitment key + fn commit(&self, v: &[G::Scalar]) -> Self::Commitment; + + /// Splits the commitment key into two pieces at a specified point + fn split_at(&self, n: usize) -> (Self, Self) + where + Self: Sized; + + /// Combines two commitment keys into one + fn combine(&self, other: &Self) -> Self; + + /// Folds the two commitment keys into one using the provided weights + fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> Self; + + /// Scales the commitment key using the provided scalar + fn scale(&self, r: &G::Scalar) -> Self; + + /// Reinterprets commitments as commitment keys + fn reinterpret_commitments_as_gens(c: &[Self::CompressedCommitment]) -> Result<Self, NovaError> + where + Self: Sized; +} + +/// Defines basic operations on commitments +pub trait CommitmentOps<Rhs = Self, Output = Self>: + Add<Rhs, Output = Output> + AddAssign<Rhs> +{ +} + +impl<T, Rhs, Output> CommitmentOps<Rhs, Output> for T where + T: Add<Rhs, Output = Output> + AddAssign<Rhs> +{ +} + +/// A helper trait for references with a commitment operation +pub trait CommitmentOpsOwned<Rhs = Self, Output = Self>: + for<'r> CommitmentOps<&'r Rhs, Output> +{ +} +impl<T, Rhs, Output> CommitmentOpsOwned<Rhs, Output> for T where + T: for<'r> CommitmentOps<&'r Rhs, Output> +{ +} + +/// A helper trait for types implementing a multiplication of a commitment with a scalar +pub trait ScalarMul<Rhs, Output = Self>: Mul<Rhs, Output = Output> + MulAssign<Rhs> {} + +impl<T, Rhs, Output> ScalarMul<Rhs, Output> for T where T: Mul<Rhs, Output = Output> + MulAssign<Rhs> +{} + +/// This trait defines the behavior of the commitment +pub trait CommitmentTrait<G: Group>: + Clone + + Copy + + Debug + + Default + + PartialEq + + Eq + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de> + + AbsorbInROTrait<G> + + AppendToTranscriptTrait + + CommitmentOps + + CommitmentOpsOwned + + ScalarMul<G::Scalar> +{ + /// Holds the type of the compressed commitment + type CompressedCommitment; + + /// Compresses self into a compressed commitment + fn compress(&self) -> Self::CompressedCommitment; + + /// Returns the coordinate representation of the commitment + fn to_coordinates(&self) -> (G::Base, G::Base, bool); +} + +/// This trait defines the behavior of a compressed commitment +pub trait CompressedCommitmentTrait<C: CompressedGroup>: + Clone + + Debug + + PartialEq + + Eq + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de> + + AppendToTranscriptTrait +{ + /// Holds the type of the commitment that can be decompressed into + type Commitment; + + /// Decompresses self into a commitment + fn decompress(&self) -> Result<Self::Commitment, NovaError>; +} + +/// A trait that ties different pieces of the commitment generation together +pub trait CommitmentEngineTrait<G: Group>: + Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> +{ + /// Holds the type of the commitment key + type CommitmentGens: CommitmentGensTrait< + G, + Commitment = Self::Commitment, + CompressedCommitment = Self::CompressedCommitment, + >; + + /// Holds the type of the commitment + type Commitment: CommitmentTrait<G, CompressedCommitment = Self::CompressedCommitment>; + + /// Holds the type of the compressed commitment + type CompressedCommitment: CompressedCommitmentTrait< + G::CompressedGroupElement, + Commitment = Self::Commitment, + >; + + /// Commits to the provided vector using the provided generators + fn commit(gens: &Self::CommitmentGens, v: &[G::Scalar]) -> Self::Commitment; +}
@@ -0,0 +1,46 @@
+//! This module defines a collection of traits that define the behavior of a polynomial evaluation engine +//! A vector of size N is treated as a multilinear polynomial in \log{N} variables, +//! and a commitment provided by the commitment engine is treated as a multilinear polynomial commitment +use crate::{ + errors::NovaError, + traits::{commitment::CommitmentEngineTrait, Group}, +}; +use merlin::Transcript; +use serde::{Deserialize, Serialize}; + +/// A trait that ties different pieces of the commitment evaluation together +pub trait EvaluationEngineTrait<G: Group>: + Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> +{ + /// A type that holds the associated commitment engine + type CE: CommitmentEngineTrait<G>; + + /// A type that holds generators + type EvaluationGens: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + + /// A type that holds the evaluation argument + type EvaluationArgument: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + + /// A method to perform any additional setup needed to produce proofs of evaluations + fn setup(gens: &<Self::CE as CommitmentEngineTrait<G>>::CommitmentGens) -> Self::EvaluationGens; + + /// A method to prove evaluations of a batch of polynomials + fn prove_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment], + polys: &[Vec<G::Scalar>], + points: &[Vec<G::Scalar>], + evals: &[G::Scalar], + ) -> Result<Self::EvaluationArgument, NovaError>; + + /// A method to verify purported evaluations of a batch of polynomials + fn verify_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comm: &[<Self::CE as CommitmentEngineTrait<G>>::Commitment], + points: &[Vec<G::Scalar>], + evals: &[G::Scalar], + arg: &Self::EvaluationArgument, + ) -> Result<(), NovaError>; +}
@@ -12,6 +12,10 @@ use merlin::Transcript;
use num_bigint::BigInt; use serde::{Deserialize, Serialize}; +pub mod commitment; + +use commitment::CommitmentEngineTrait; + /// Represents an element of a group /// This is currently tailored for an elliptic curve group pub trait Group:@@ -47,7 +51,7 @@ + Serialize
+ for<'de> Deserialize<'de>; /// A type representing preprocessed group element - type PreprocessedGroupElement: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type PreprocessedGroupElement: Clone + Debug + 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@@ -55,6 +59,9 @@ type RO: ROTrait<Self::Base, Self::Scalar> + Serialize + for<'de> Deserialize<'de>;
/// An alternate implementation of Self::RO in the circuit model type ROCircuit: ROCircuitTrait<Self::Base> + Serialize + for<'de> Deserialize<'de>; + + /// A type that defines a commitment engine over scalars in the group + type CE: CommitmentEngineTrait<Self> + Serialize + for<'de> Deserialize<'de>; /// A method to compute a multiexponentation fn vartime_multiscalar_mul(@@ -212,4 +219,5 @@ }
} pub mod circuit; +pub mod evaluation; pub mod snark;