diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 31ac63a..f311d2b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,12 +17,8 @@ jobs: 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 diff --git a/Cargo.toml b/Cargo.toml index 0db164b..0897783 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nova-snark" -version = "0.12.0" +version = "0.13.0" authors = ["Srinath Setty "] edition = "2021" description = "Recursive zkSNARKs without trusted setup" @@ -13,7 +13,7 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] [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" diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index 634aa64..63101b7 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -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; -type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; +type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine; +type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine; +type S1 = nova_snark::spartan::RelaxedR1CSSNARK; +type S2 = nova_snark::spartan::RelaxedR1CSSNARK; type C1 = NonTrivialTestCircuit<::Scalar>; type C2 = TrivialTestCircuit<::Scalar>; diff --git a/examples/minroot.rs b/examples/minroot.rs index e93a9d0..25259f1 100644 --- a/examples/minroot.rs +++ b/examples/minroot.rs @@ -257,8 +257,11 @@ fn main() { // 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; - type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; + type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine; + type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine; + type S1 = nova_snark::spartan::RelaxedR1CSSNARK; + type S2 = nova_snark::spartan::RelaxedR1CSSNARK; + let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark); println!( "CompressedSNARK::prove: {:?}, took {:?}", diff --git a/examples/signature.rs b/examples/signature.rs index f6b0c6d..262d84f 100644 --- a/examples/signature.rs +++ b/examples/signature.rs @@ -182,7 +182,6 @@ pub fn synthesize_bits>( bits: Option>, ) -> Result, SynthesisError> { (0..F::NUM_BITS) - .into_iter() .map(|i| { AllocatedBit::alloc( cs.namespace(|| format!("bit {i}")), diff --git a/src/circuit.rs b/src/circuit.rs index ec708d4..30d11f7 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -6,8 +6,7 @@ //! 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 @@ use super::{ }, }, 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 @@ pub struct NovaAugmentedCircuitInputs { T: Option>, } -impl NovaAugmentedCircuitInputs -where - G: Group, -{ +impl NovaAugmentedCircuitInputs { /// Create new inputs/witness for the verification circuit #[allow(clippy::too_many_arguments)] pub fn new( @@ -88,22 +87,14 @@ where /// 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 -where - G: Group, - SC: StepCircuit, -{ +pub struct NovaAugmentedCircuit> { params: NovaAugmentedCircuitParams, ro_consts: ROConstantsCircuit, inputs: Option>, step_circuit: SC, // The function that is applied for each step } -impl NovaAugmentedCircuit -where - G: Group, - SC: StepCircuit, -{ +impl> NovaAugmentedCircuit { /// Create a new verification circuit for the input relaxed r1cs instances pub fn new( params: NovaAugmentedCircuitParams, @@ -186,10 +177,7 @@ where 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 @@ where } } -impl Circuit<::Base> for NovaAugmentedCircuit -where - G: Group, - SC: StepCircuit, +impl> Circuit<::Base> + for NovaAugmentedCircuit { fn synthesize::Base>>( self, @@ -388,10 +374,11 @@ mod tests { 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}, }; diff --git a/src/gadgets/r1cs.rs b/src/gadgets/r1cs.rs index 420e2c8..8a73685 100644 --- a/src/gadgets/r1cs.rs +++ b/src/gadgets/r1cs.rs @@ -13,7 +13,7 @@ use crate::{ }, }, 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 -where - G: Group, -{ +pub struct AllocatedR1CSInstance { pub(crate) W: AllocatedPoint, pub(crate) X0: AllocatedNum, pub(crate) X1: AllocatedNum, } -impl AllocatedR1CSInstance -where - G: Group, -{ +impl AllocatedR1CSInstance { /// Takes the r1cs instance and creates a new allocated r1cs instance pub fn alloc::Base>>( mut cs: CS, @@ -44,8 +38,7 @@ where // 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::( @@ -71,10 +64,7 @@ where } /// An Allocated Relaxed R1CS Instance -pub struct AllocatedRelaxedR1CSInstance -where - G: Group, -{ +pub struct AllocatedRelaxedR1CSInstance { pub(crate) W: AllocatedPoint, pub(crate) E: AllocatedPoint, pub(crate) u: AllocatedNum, @@ -82,10 +72,7 @@ where pub(crate) X1: BigNat, } -impl AllocatedRelaxedR1CSInstance -where - G: Group, -{ +impl AllocatedRelaxedR1CSInstance { /// Allocates the given RelaxedR1CSInstance as a witness of the circuit pub fn alloc::Base>>( mut cs: CS, @@ -97,14 +84,14 @@ where 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. diff --git a/src/lib.rs b/src/lib.rs index e99a5a4..dc3cfcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ use r1cs::{ }; 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 @@ where } } +type CommitmentGens = <::CE as CommitmentEngineTrait>::CommitmentGens; +type Commitment = <::CE as CommitmentEngineTrait>::Commitment; +type CompressedCommitment = <::CE as CommitmentEngineTrait>::CompressedCommitment; +type CE = ::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; - type S2 = spartan_with_ipa_pc::RelaxedR1CSSNARK; + type EE1 = provider::ipa_pc::EvaluationEngine; + type EE2 = provider::ipa_pc::EvaluationEngine; + type S1 = spartan::RelaxedR1CSSNARK; + type S2 = spartan::RelaxedR1CSSNARK; use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; use core::marker::PhantomData; use ff::PrimeField; diff --git a/src/nifs.rs b/src/nifs.rs index 9ef010a..aead810 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -2,12 +2,15 @@ #![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 @@ 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, Serialize, Deserialize)] +#[serde(bound = "")] pub struct NIFS { - pub(crate) comm_T: CompressedCommitment, + pub(crate) comm_T: CompressedCommitment, _p: PhantomData, } diff --git a/src/spartan_with_ipa_pc/ipa.rs b/src/provider/ipa_pc.rs similarity index 62% rename from src/spartan_with_ipa_pc/ipa.rs rename to src/provider/ipa_pc.rs index 3452027..44467ff 100644 --- a/src/spartan_with_ipa_pc/ipa.rs +++ b/src/provider/ipa_pc.rs @@ -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(a: &[T], b: &[T]) -> T +/// Provides an implementation of generators for proving evaluations +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct EvaluationGens { + gens_v: CommitmentGens, + gens_s: CommitmentGens, +} + +/// Provides an implementation of a polynomial evaluation argument +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +pub struct EvaluationArgument { + nifs: Vec>, + ipa: InnerProductArgument, +} + +/// Provides an implementation of a polynomial evaluation engine using IPA +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EvaluationEngine { + _p: PhantomData, +} + +impl EvaluationEngineTrait for EvaluationEngine { + type CE = G::CE; + type EvaluationGens = EvaluationGens; + type EvaluationArgument = EvaluationArgument; + + fn setup(gens: &>::CommitmentGens) -> Self::EvaluationGens { + EvaluationGens { + gens_v: gens.clone(), + gens_s: CommitmentGens::::new(b"ipa", 1), + } + } + + fn prove_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comms: &[Commitment], + polys: &[Vec], + points: &[Vec], + evals: &[G::Scalar], + ) -> Result { + // 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: &[>::Commitment], + points: &[Vec], + 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(a: &[T], b: &[T]) -> T where T: Field + Send + Sync, { @@ -29,7 +161,7 @@ pub struct InnerProductInstance { } impl InnerProductInstance { - pub fn new(comm_a_vec: &Commitment, b_vec: &[G::Scalar], c: &G::Scalar) -> Self { + fn new(comm_a_vec: &Commitment, 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 @@ impl InnerProductInstance { } } - pub fn pad(&self, n: usize) -> InnerProductInstance { + fn pad(&self, n: usize) -> InnerProductInstance { let mut b_vec = self.b_vec.clone(); b_vec.resize(n, G::Scalar::zero()); InnerProductInstance { @@ -48,18 +180,18 @@ impl InnerProductInstance { } } -pub struct InnerProductWitness { +struct InnerProductWitness { a_vec: Vec, } impl InnerProductWitness { - 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 { + fn pad(&self, n: usize) -> InnerProductWitness { let mut a_vec = self.a_vec.clone(); a_vec.resize(n, G::Scalar::zero()); InnerProductWitness { a_vec } @@ -67,17 +199,17 @@ impl InnerProductWitness { } /// A non-interactive folding scheme (NIFS) for inner product relations -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct NIFSForInnerProduct { cross_term: G::Scalar, } impl NIFSForInnerProduct { - pub fn protocol_name() -> &'static [u8] { + fn protocol_name() -> &'static [u8] { b"NIFSForInnerProduct" } - pub fn prove( + fn prove( U1: &InnerProductInstance, W1: &InnerProductWitness, U2: &InnerProductInstance, @@ -136,7 +268,7 @@ impl NIFSForInnerProduct { (NIFSForInnerProduct { cross_term }, U, W) } - pub fn verify( + fn verify( &self, U1: &InnerProductInstance, U2: &InnerProductInstance, @@ -183,10 +315,11 @@ impl NIFSForInnerProduct { } /// An inner product argument -#[derive(Debug, Serialize, Deserialize)] -pub struct InnerProductArgument { - L_vec: Vec>, - R_vec: Vec>, +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound = "")] +struct InnerProductArgument { + L_vec: Vec>, + R_vec: Vec>, a_hat: G::Scalar, _p: PhantomData, } @@ -196,9 +329,9 @@ impl InnerProductArgument { b"inner product argument" } - pub fn prove( - gens: &CommitGens, - gens_c: &CommitGens, + fn prove( + gens: &CommitmentGens, + gens_c: &CommitmentGens, U: &InnerProductInstance, W: &InnerProductWitness, transcript: &mut Transcript, @@ -220,15 +353,15 @@ impl InnerProductArgument { // 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, + gens: &CommitmentGens, transcript: &mut Transcript| -> Result< ( - CompressedCommitment, - CompressedCommitment, + CompressedCommitment, + CompressedCommitment, Vec, Vec, - CommitGens, + CommitmentGens, ), NovaError, > { @@ -238,20 +371,24 @@ impl InnerProductArgument { 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::>() - .commit(&gens_R.combine(&gens_c)) - .compress(); - let R = a_vec[n / 2..n] - .iter() - .chain(iter::once(&c_R)) - .copied() - .collect::>() - .commit(&gens_L.combine(&gens_c)) - .compress(); + let L = CE::::commit( + &gens_R.combine(&gens_c), + &a_vec[0..n / 2] + .iter() + .chain(iter::once(&c_L)) + .copied() + .collect::>(), + ) + .compress(); + let R = CE::::commit( + &gens_L.combine(&gens_c), + &a_vec[n / 2..n] + .iter() + .chain(iter::once(&c_R)) + .copied() + .collect::>(), + ) + .compress(); L.append_to_transcript(b"L", transcript); R.append_to_transcript(b"R", transcript); @@ -278,8 +415,8 @@ impl InnerProductArgument { }; // two vectors to hold the logarithmic number of group elements - let mut L_vec: Vec> = Vec::new(); - let mut R_vec: Vec> = Vec::new(); + let mut L_vec: Vec> = Vec::new(); + let mut R_vec: Vec> = Vec::new(); // we create mutable copies of vectors and generators let mut a_vec = W.a_vec.to_vec(); @@ -304,10 +441,10 @@ impl InnerProductArgument { }) } - pub fn verify( + fn verify( &self, - gens: &CommitGens, - gens_c: &CommitGens, + gens: &CommitmentGens, + gens_c: &CommitmentGens, n: usize, U: &InnerProductInstance, transcript: &mut Transcript, @@ -329,7 +466,7 @@ impl InnerProductArgument { 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::::commit(&gens_c, &[U.c]); let batch_invert = |v: &[G::Scalar]| -> Result, NovaError> { let mut products = vec![G::Scalar::zero(); v.len()]; @@ -396,29 +533,37 @@ impl InnerProductArgument { }; let gens_hat = { - let c = s.commit(gens).compress(); - CommitGens::reinterpret_commitments_as_gens(&[c])? + let c = CE::::commit(gens, &s).compress(); + CommitmentGens::::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::::reinterpret_commitments_as_gens(&self.L_vec)?; + let gens_R = CommitmentGens::::reinterpret_commitments_as_gens(&self.R_vec)?; + let gens_P = CommitmentGens::::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::>() - .commit(&gens_folded) + + CE::::commit( + &gens_folded, + &r_square + .iter() + .chain(r_inverse_square.iter()) + .chain(iter::once(&G::Scalar::one())) + .copied() + .collect::>(), + ) }; - if P_hat == [self.a_hat, self.a_hat * b_hat].commit(&gens_hat.combine(&gens_c)) { + if P_hat + == CE::::commit( + &gens_hat.combine(&gens_c), + &[self.a_hat, self.a_hat * b_hat], + ) + { Ok(()) } else { Err(NovaError::InvalidIPA) diff --git a/src/provider/mod.rs b/src/provider/mod.rs new file mode 100644 index 0000000..c6d7a0d --- /dev/null +++ b/src/provider/mod.rs @@ -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; diff --git a/src/pasta.rs b/src/provider/pasta.rs similarity index 98% rename from src/pasta.rs rename to src/provider/pasta.rs index f882229..4a47b9c 100644 --- a/src/pasta.rs +++ b/src/provider/pasta.rs @@ -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 @@ macro_rules! impl_traits { type PreprocessedGroupElement = $name::Affine; type RO = PoseidonRO; type ROCircuit = PoseidonROCircuit; + type CE = CommitmentEngine; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] fn vartime_multiscalar_mul( diff --git a/src/commitments.rs b/src/provider/pedersen.rs similarity index 67% rename from src/commitments.rs rename to src/provider/pedersen.rs index 4415daa..ba15496 100644 --- a/src/commitments.rs +++ b/src/provider/pedersen.rs @@ -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 { +/// A type that holds commitment generators +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CommitmentGens { gens: Vec, _p: PhantomData, } +/// A type that holds a commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct Commitment { 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 { comm: C, } -impl CommitGens { - pub fn new(label: &'static [u8], n: usize) -> Self { - CommitGens { +impl CommitmentGensTrait for CommitmentGens { + type Commitment = Commitment; + type CompressedCommitment = CompressedCommitment; + + 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 @@ impl CommitGens { self.gens.len() } - pub fn split_at(&self, n: usize) -> (CommitGens, CommitGens) { + 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, CommitmentGens) { ( - 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) -> CommitGens { + fn combine(&self, other: &CommitmentGens) -> CommitmentGens { 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 { + fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitmentGens { 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:: { + let gens = CommitmentGens:: { 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:: { + let gens = CommitmentGens:: { 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], ) -> Result { let d = (0..c.len()) @@ -122,19 +141,25 @@ impl CommitGens { .into_par_iter() .map(|i| d[i].comm.preprocessed()) .collect(); - Ok(CommitGens { + Ok(CommitmentGens { gens, _p: Default::default(), }) } } -impl Commitment { - pub fn compress(&self) -> CompressedCommitment { +impl CommitmentTrait for Commitment { + type CompressedCommitment = CompressedCommitment; + + fn compress(&self) -> CompressedCommitment { CompressedCommitment { comm: self.comm.compress(), } } + + fn to_coordinates(&self) -> (G::Base, G::Base, bool) { + self.comm.to_coordinates() + } } impl Default for Commitment { @@ -143,8 +168,10 @@ impl Default for Commitment { } } -impl CompressedCommitment { - pub fn decompress(&self) -> Result, NovaError> { +impl CompressedCommitmentTrait for CompressedCommitment { + type Commitment = Commitment; + + fn decompress(&self) -> Result { let comm = self.comm.decompress(); if comm.is_none() { return Err(NovaError::DecompressionError); @@ -155,19 +182,6 @@ impl CompressedCommitment { } } -pub trait CommitTrait { - fn commit(&self, gens: &CommitGens) -> Commitment; -} - -impl CommitTrait for [G::Scalar] { - fn commit(&self, gens: &CommitGens) -> Commitment { - assert!(gens.gens.len() >= self.len()); - Commitment { - comm: G::vartime_multiscalar_mul(self, &gens.gens[..self.len()]), - } - } -} - impl AppendToTranscriptTrait for Commitment { fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { transcript.append_message(label, self.comm.compress().as_bytes()); @@ -193,8 +207,8 @@ impl AppendToTranscriptTrait for CompressedCommitment { } } -impl<'b, G: Group> MulAssign<&'b G::Scalar> for Commitment { - fn mul_assign(&mut self, scalar: &'b G::Scalar) { +impl MulAssign for Commitment { + fn mul_assign(&mut self, scalar: G::Scalar) { let result = (self as &Commitment).comm * scalar; *self = Commitment { comm: result }; } @@ -272,3 +286,19 @@ macro_rules! define_add_assign_variants { define_add_assign_variants!(G = Group, LHS = Commitment, RHS = Commitment); define_add_variants!(G = Group, LHS = Commitment, RHS = Commitment, Output = Commitment); + +/// Provides a commitment engine +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CommitmentEngine { + _p: PhantomData, +} + +impl CommitmentEngineTrait for CommitmentEngine { + type CommitmentGens = CommitmentGens; + type Commitment = Commitment; + type CompressedCommitment = CompressedCommitment; + + fn commit(gens: &Self::CommitmentGens, v: &[G::Scalar]) -> Self::Commitment { + gens.commit(v) + } +} diff --git a/src/poseidon.rs b/src/provider/poseidon.rs similarity index 99% rename from src/poseidon.rs rename to src/provider/poseidon.rs index 0fa2b90..7df4620 100644 --- a/src/poseidon.rs +++ b/src/provider/poseidon.rs @@ -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}, diff --git a/src/r1cs.rs b/src/r1cs.rs index e046565..a6de17f 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -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 @@ use sha3::{Digest, Sha3_256}; #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] pub struct R1CSGens { - pub(crate) gens: CommitGens, + pub(crate) gens: CommitmentGens, } /// A type that holds the shape of the R1CS matrices @@ -71,7 +76,7 @@ impl R1CSGens { /// Samples public parameters for the specified number of constraints and variables in an R1CS pub fn new(num_cons: usize, num_vars: usize) -> R1CSGens { R1CSGens { - gens: CommitGens::new(b"gens", max(num_vars, num_cons)), + gens: CommitmentGens::::new(b"gens", max(num_vars, num_cons)), } } } @@ -202,7 +207,10 @@ impl R1CSShape { // 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::::commit(&gens.gens, &W.W), + || CE::::commit(&gens.gens, &W.E), + ); U.comm_W == comm_W && U.comm_E == comm_E }; @@ -239,7 +247,7 @@ impl R1CSShape { }; // 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 @@ impl R1CSShape { .map(|(((a, b), c), d)| *a + *b - *c - *d) .collect::>(); - let comm_T = T.commit(&gens.gens); + let comm_T = gens.gens.commit(&T); Ok((T, comm_T)) } @@ -458,7 +466,7 @@ impl R1CSWitness { /// Commits to the witness using the supplied generators pub fn commit(&self, gens: &R1CSGens) -> Commitment { - self.W.commit(&gens.gens) + gens.gens.commit(&self.W) } } @@ -515,7 +523,10 @@ impl RelaxedR1CSWitness { /// Commits to the witness using the supplied generators pub fn commit(&self, gens: &R1CSGens) -> (Commitment, Commitment) { - (self.W.commit(&gens.gens), self.E.commit(&gens.gens)) + ( + CE::::commit(&gens.gens, &self.W), + CE::::commit(&gens.gens, &self.E), + ) } /// Folds an incoming R1CSWitness into the current one @@ -566,7 +577,7 @@ impl RelaxedR1CSWitness { impl RelaxedR1CSInstance { /// Produces a default RelaxedR1CSInstance given R1CSGens and R1CSShape pub fn default(_gens: &R1CSGens, S: &R1CSShape) -> RelaxedR1CSInstance { - let (comm_W, comm_E) = (Commitment::default(), Commitment::default()); + let (comm_W, comm_E) = (Commitment::::default(), Commitment::::default()); RelaxedR1CSInstance { comm_W, comm_E, @@ -605,7 +616,7 @@ impl RelaxedR1CSInstance { .zip(X2) .map(|(a, b)| *a + *r * *b) .collect::>(); - 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; diff --git a/src/spartan_with_ipa_pc/mod.rs b/src/spartan/mod.rs similarity index 84% rename from src/spartan_with_ipa_pc/mod.rs rename to src/spartan/mod.rs index 7f68340..195e251 100644 --- a/src/spartan_with_ipa_pc/mod.rs +++ b/src/spartan/mod.rs @@ -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 @@ 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, +pub struct ProverKey> { + gens: EE::EvaluationGens, S: R1CSShape, } -impl ProverKeyTrait for ProverKey { +impl> ProverKeyTrait for ProverKey { fn new(gens: &R1CSGens, S: &R1CSShape) -> 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 @@ 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, +pub struct VerifierKey> { + gens: EE::EvaluationGens, S: R1CSShape, } -impl VerifierKeyTrait for VerifierKey { +impl> VerifierKeyTrait + for VerifierKey +{ fn new(gens: &R1CSGens, S: &R1CSShape) -> 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 @@ impl VerifierKeyTrait for VerifierKey { /// the commitment to a vector viewed as a polynomial commitment #[derive(Serialize, Deserialize)] #[serde(bound = "")] -pub struct RelaxedR1CSSNARK { +pub struct RelaxedR1CSSNARK> { sc_proof_outer: SumcheckProof, claims_outer: (G::Scalar, G::Scalar, G::Scalar), sc_proof_inner: SumcheckProof, eval_E: G::Scalar, eval_W: G::Scalar, - nifs_ip: NIFSForInnerProduct, - ipa: InnerProductArgument, + eval_arg: EE::EvaluationArgument, } -impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { - type ProverKey = ProverKey; - type VerifierKey = VerifierKey; +impl> RelaxedR1CSSNARKTrait + for RelaxedR1CSSNARK +{ + type ProverKey = ProverKey; + type VerifierKey = VerifierKey; /// produces a succinct proof of satisfiability of a RelaxedR1CS instance fn prove( @@ -88,8 +84,6 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { ) -> Result { 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 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { 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), - &mut transcript, - ); - - let ipa = InnerProductArgument::prove( - &pk.gens_r1cs.gens, - &pk.gens_ipa, - &r_U, - &r_W, + let eval_arg = EE::prove_batch( + &pk.gens, &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 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { sc_proof_inner, eval_W, eval_E, - nifs_ip, - ipa, + eval_arg, }) } @@ -366,22 +348,13 @@ impl RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { // 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, - ), - &mut transcript, - ); - - self.ipa.verify( - &vk.gens_r1cs.gens, - &vk.gens_ipa, - max(vk.S.num_vars, vk.S.num_cons), - &r_U, + EE::verify_batch( + &vk.gens, &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(()) diff --git a/src/spartan_with_ipa_pc/polynomial.rs b/src/spartan/polynomial.rs similarity index 91% rename from src/spartan_with_ipa_pc/polynomial.rs rename to src/spartan/polynomial.rs index 8586483..7706119 100644 --- a/src/spartan_with_ipa_pc/polynomial.rs +++ b/src/spartan/polynomial.rs @@ -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 { +pub(crate) struct EqPolynomial { r: Vec, } impl EqPolynomial { + /// Creates a new polynomial from its succinct specification pub fn new(r: Vec) -> 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 @@ impl EqPolynomial { } #[derive(Debug)] -pub struct MultilinearPolynomial { +pub(crate) struct MultilinearPolynomial { num_vars: usize, // the number of variables in the multilinear polynomial Z: Vec, // evaluations of the polynomial in all the 2^num_vars Boolean inputs } @@ -105,7 +108,7 @@ impl Index for MultilinearPolynomial { } } -pub struct SparsePolynomial { +pub(crate) struct SparsePolynomial { num_vars: usize, Z: Vec<(usize, Scalar)>, } diff --git a/src/spartan_with_ipa_pc/sumcheck.rs b/src/spartan/sumcheck.rs similarity index 99% rename from src/spartan_with_ipa_pc/sumcheck.rs rename to src/spartan/sumcheck.rs index dad4bcb..deb29f0 100644 --- a/src/spartan_with_ipa_pc/sumcheck.rs +++ b/src/spartan/sumcheck.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] #[serde(bound = "")] -pub struct SumcheckProof { +pub(crate) struct SumcheckProof { compressed_polys: Vec>, } diff --git a/src/traits/commitment.rs b/src/traits/commitment.rs new file mode 100644 index 0000000..8dc1ef2 --- /dev/null +++ b/src/traits/commitment.rs @@ -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: + 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 + where + Self: Sized; +} + +/// Defines basic operations on commitments +pub trait CommitmentOps: + Add + AddAssign +{ +} + +impl CommitmentOps for T where + T: Add + AddAssign +{ +} + +/// A helper trait for references with a commitment operation +pub trait CommitmentOpsOwned: + for<'r> CommitmentOps<&'r Rhs, Output> +{ +} +impl CommitmentOpsOwned 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: Mul + MulAssign {} + +impl ScalarMul for T where T: Mul + MulAssign +{} + +/// This trait defines the behavior of the commitment +pub trait CommitmentTrait: + Clone + + Copy + + Debug + + Default + + PartialEq + + Eq + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de> + + AbsorbInROTrait + + AppendToTranscriptTrait + + CommitmentOps + + CommitmentOpsOwned + + ScalarMul +{ + /// 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: + 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; +} + +/// A trait that ties different pieces of the commitment generation together +pub trait CommitmentEngineTrait: + 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; + + /// 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; +} diff --git a/src/traits/evaluation.rs b/src/traits/evaluation.rs new file mode 100644 index 0000000..431ec96 --- /dev/null +++ b/src/traits/evaluation.rs @@ -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: + Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> +{ + /// A type that holds the associated commitment engine + type CE: CommitmentEngineTrait; + + /// 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: &>::CommitmentGens) -> Self::EvaluationGens; + + /// A method to prove evaluations of a batch of polynomials + fn prove_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comm: &[>::Commitment], + polys: &[Vec], + points: &[Vec], + evals: &[G::Scalar], + ) -> Result; + + /// A method to verify purported evaluations of a batch of polynomials + fn verify_batch( + gens: &Self::EvaluationGens, + transcript: &mut Transcript, + comm: &[>::Commitment], + points: &[Vec], + evals: &[G::Scalar], + arg: &Self::EvaluationArgument, + ) -> Result<(), NovaError>; +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 516bd1e..98251b8 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -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 @@ pub trait Group: + 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 @@ -56,6 +60,9 @@ pub trait Group: /// An alternate implementation of Self::RO in the circuit model type ROCircuit: ROCircuitTrait + Serialize + for<'de> Deserialize<'de>; + /// A type that defines a commitment engine over scalars in the group + type CE: CommitmentEngineTrait + Serialize + for<'de> Deserialize<'de>; + /// A method to compute a multiexponentation fn vartime_multiscalar_mul( scalars: &[Self::Scalar], @@ -212,4 +219,5 @@ impl AppendToTranscriptTrait for [F] { } pub mod circuit; +pub mod evaluation; pub mod snark;