diff --git a/CHANGELOG.md b/CHANGELOG.md index 0792850..9f8719d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Features +- [\#34](https://github.com/arkworks-rs/groth16/pull/34) Allow specifying custom R1CS to QAP reductions. + ### Improvements ### Bug fixes @@ -27,19 +29,23 @@ ## v0.2.0 ### Breaking changes -- [\#4](https://github.com/arkworks-rs/groth16/pull/4) Change groth16's logic to implement the `SNARK` trait. + +- [\#4](https://github.com/arkworks-rs/groth16/pull/4) Change `groth16`'s logic to implement the `SNARK` trait. - Minimum version on crates from `arkworks-rs/algebra` and `arkworks-rs/curves` is now `v0.2.0` - [\#24](https://github.com/arkworks-rs/groth16/pull/24) Switch from `bench-utils` to `ark_std::perf_trace` ### Features + - [\#5](https://github.com/arkworks-rs/groth16/pull/5) Add R1CS constraints for the groth16 verifier. - [\#8](https://github.com/arkworks-rs/groth16/pull/8) Add benchmarks for the prover - [\#16](https://github.com/arkworks-rs/groth16/pull/16) Add proof re-randomization ### Improvements + - [\#9](https://github.com/arkworks-rs/groth16/pull/9) Improve memory consumption by manually dropping large vectors once they're no longer needed ### Bug fixes + - [c9bc5519](https://github.com/arkworks-rs/groth16/commit/885b9b569522f59a7eb428d1095f442ec9bc5519) Fix parallel feature flag - [\#22](https://github.com/arkworks-rs/groth16/pull/22) Compile with `panic='abort'` in release mode, for safety of the library across FFI boundaries. diff --git a/src/generator.rs b/src/generator.rs index aaabb75..9cc7d6b 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,4 +1,7 @@ -use crate::{r1cs_to_qap::R1CStoQAP, ProvingKey, Vec, VerifyingKey}; +use crate::{ + r1cs_to_qap::{LibsnarkReduction, R1CStoQAP}, + ProvingKey, Vec, VerifyingKey, +}; use ark_ec::{msm::FixedBaseMSM, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, PrimeField, UniformRand, Zero}; use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; @@ -12,14 +15,30 @@ use ark_std::{cfg_into_iter, cfg_iter}; #[cfg(feature = "parallel")] use rayon::prelude::*; +#[inline] /// Generates a random common reference string for /// a circuit. -#[inline] pub fn generate_random_parameters(circuit: C, rng: &mut R) -> R1CSResult> where E: PairingEngine, C: ConstraintSynthesizer, R: Rng, +{ + generate_random_parameters_with_reduction::(circuit, rng) +} + +/// Generates a random common reference string for +/// a circuit using the provided R1CS-to-QAP reduction. +#[inline] +pub fn generate_random_parameters_with_reduction( + circuit: C, + rng: &mut R, +) -> R1CSResult> +where + E: PairingEngine, + C: ConstraintSynthesizer, + R: Rng, + QAP: R1CStoQAP, { let alpha = E::Fr::rand(rng); let beta = E::Fr::rand(rng); @@ -29,7 +48,7 @@ where let g1_generator = E::G1Projective::rand(rng); let g2_generator = E::G2Projective::rand(rng); - generate_parameters::( + generate_parameters_with_qap::( circuit, alpha, beta, @@ -41,7 +60,7 @@ where ) } -/// Create parameters for a circuit, given some toxic waste and group generators +/// Create parameters for a circuit, given some toxic waste, and group generators pub fn generate_parameters( circuit: C, alpha: E::Fr, @@ -56,6 +75,35 @@ where E: PairingEngine, C: ConstraintSynthesizer, R: Rng, +{ + generate_parameters_with_qap::( + circuit, + alpha, + beta, + gamma, + delta, + g1_generator, + g2_generator, + rng, + ) +} + +/// Create parameters for a circuit, given some toxic waste, R1CS to QAP calculator and group generators +pub fn generate_parameters_with_qap( + circuit: C, + alpha: E::Fr, + beta: E::Fr, + gamma: E::Fr, + delta: E::Fr, + g1_generator: E::G1Projective, + g2_generator: E::G2Projective, + rng: &mut R, +) -> R1CSResult> +where + E: PairingEngine, + C: ConstraintSynthesizer, + R: Rng, + QAP: R1CStoQAP, { type D = GeneralEvaluationDomain; @@ -86,7 +134,7 @@ where let reduction_time = start_timer!(|| "R1CS to QAP Instance Map with Evaluation"); let num_instance_variables = cs.num_instance_variables(); let (a, b, c, zt, qap_num_variables, m_raw) = - R1CStoQAP::instance_map_with_evaluation::>(cs, &t)?; + QAP::instance_map_with_evaluation::>(cs, &t)?; end_timer!(reduction_time); // Compute query densities @@ -168,9 +216,7 @@ where scalar_bits, g1_window, &g1_table, - &cfg_into_iter!(0..m_raw - 1) - .map(|i| zt * &delta_inverse * &t.pow([i as u64])) - .collect::>(), + &QAP::h_query_scalars::<_, D>(m_raw - 1, t, zt, delta_inverse)?, ); end_timer!(h_time); diff --git a/src/lib.rs b/src/lib.rs index def723a..a474681 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ extern crate ark_std; extern crate derivative; /// Reduce an R1CS instance to a *Quadratic Arithmetic Program* instance. -pub(crate) mod r1cs_to_qap; +pub mod r1cs_to_qap; /// Data structures used by the prover, verifier, and generator. pub mod data_structures; diff --git a/src/prover.rs b/src/prover.rs index 27e0bc9..dbe9e3f 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -1,4 +1,7 @@ -use crate::{r1cs_to_qap::R1CStoQAP, Proof, ProvingKey, VerifyingKey}; +use crate::{ + r1cs_to_qap::{LibsnarkReduction, R1CStoQAP}, + Proof, ProvingKey, VerifyingKey, +}; use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, PrimeField, UniformRand, Zero}; use ark_poly::GeneralEvaluationDomain; @@ -13,7 +16,6 @@ use rayon::prelude::*; /// Create a Groth16 proof that is zero-knowledge. /// This method samples randomness for zero knowledges via `rng`. -#[inline] pub fn create_random_proof( circuit: C, pk: &ProvingKey, @@ -23,25 +25,58 @@ where E: PairingEngine, C: ConstraintSynthesizer, R: Rng, +{ + create_random_proof_with_reduction::(circuit, pk, rng) +} + +/// Create a Groth16 proof that is zero-knowledge using the provided +/// R1CS-to-QAP reduction. +/// This method samples randomness for zero knowledges via `rng`. +#[inline] +pub fn create_random_proof_with_reduction( + circuit: C, + pk: &ProvingKey, + rng: &mut R, +) -> R1CSResult> +where + E: PairingEngine, + C: ConstraintSynthesizer, + R: Rng, + QAP: R1CStoQAP, { let r = E::Fr::rand(rng); let s = E::Fr::rand(rng); - create_proof::(circuit, pk, r, s) + create_proof_with_reduction::(circuit, pk, r, s) } -/// Create a Groth16 proof that is *not* zero-knowledge. #[inline] +/// Create a Groth16 proof that is *not* zero-knowledge. pub fn create_proof_no_zk(circuit: C, pk: &ProvingKey) -> R1CSResult> where E: PairingEngine, C: ConstraintSynthesizer, { - create_proof::(circuit, pk, E::Fr::zero(), E::Fr::zero()) + create_proof_with_reduction_no_zk::(circuit, pk) } -/// Create a Groth16 proof using randomness `r` and `s`. +/// Create a Groth16 proof that is *not* zero-knowledge with the provided +/// R1CS-to-QAP reduction. #[inline] +pub fn create_proof_with_reduction_no_zk( + circuit: C, + pk: &ProvingKey, +) -> R1CSResult> +where + E: PairingEngine, + C: ConstraintSynthesizer, + QAP: R1CStoQAP, +{ + create_proof_with_reduction::(circuit, pk, E::Fr::zero(), E::Fr::zero()) +} + +#[inline] +/// Create a Groth16 proof using randomness `r` and `s`. pub fn create_proof( circuit: C, pk: &ProvingKey, @@ -51,6 +86,23 @@ pub fn create_proof( where E: PairingEngine, C: ConstraintSynthesizer, +{ + create_proof_with_reduction::(circuit, pk, r, s) +} + +/// Create a Groth16 proof using randomness `r` and `s` and the provided +/// R1CS-to-QAP reduction. +#[inline] +pub fn create_proof_with_reduction( + circuit: C, + pk: &ProvingKey, + r: E::Fr, + s: E::Fr, +) -> R1CSResult> +where + E: PairingEngine, + C: ConstraintSynthesizer, + QAP: R1CStoQAP, { type D = GeneralEvaluationDomain; @@ -71,7 +123,7 @@ where end_timer!(lc_time); let witness_map_time = start_timer!(|| "R1CS to QAP witness map"); - let h = R1CStoQAP::witness_map::>(cs.clone())?; + let h = QAP::witness_map::>(cs.clone())?; end_timer!(witness_map_time); let h_assignment = cfg_into_iter!(h).map(|s| s.into()).collect::>(); let c_acc_time = start_timer!(|| "Compute C"); diff --git a/src/r1cs_to_qap.rs b/src/r1cs_to_qap.rs index 9ff4e82..7032970 100644 --- a/src/r1cs_to_qap.rs +++ b/src/r1cs_to_qap.rs @@ -10,7 +10,8 @@ use core::ops::{AddAssign, Deref}; use rayon::prelude::*; #[inline] -fn evaluate_constraint<'a, LHS, RHS, R>(terms: &'a [(LHS, usize)], assignment: &'a [RHS]) -> R +/// Computes the inner product of `terms` with `assignment`. +pub fn evaluate_constraint<'a, LHS, RHS, R>(terms: &'a [(LHS, usize)], assignment: &'a [RHS]) -> R where LHS: One + Send + Sync + PartialEq, RHS: Send + Sync + core::ops::Mul<&'a LHS, Output = RHS> + Copy, @@ -41,12 +42,37 @@ where return res; } -pub(crate) struct R1CStoQAP; +/// Computes instance and witness reductions from R1CS to +/// Quadratic Arithmetic Programs (QAPs). +pub trait R1CStoQAP { + /// Computes a QAP instance corresponding to the R1CS instance defined by `cs`. + fn instance_map_with_evaluation>( + cs: ConstraintSystemRef, + t: &F, + ) -> Result<(Vec, Vec, Vec, F, usize, usize), SynthesisError>; + + /// Computes a QAP witness corresponding to the R1CS witness defined by `cs`. + fn witness_map>( + prover: ConstraintSystemRef, + ) -> Result, SynthesisError>; + + /// Computes the exponents that the generator uses to calculate base + /// elements which the prover later uses to compute `h(x)t(x)/delta`. + fn h_query_scalars>( + max_power: usize, + t: F, + zt: F, + delta_inverse: F, + ) -> Result, SynthesisError>; +} + +/// Computes the R1CS-to-QAP reduction defined in [`libsnark`](https://github.com/scipr-lab/libsnark/blob/2af440246fa2c3d0b1b0a425fb6abd8cc8b9c54d/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.tcc). +pub struct LibsnarkReduction; -impl R1CStoQAP { +impl R1CStoQAP for LibsnarkReduction { #[inline] #[allow(clippy::type_complexity)] - pub(crate) fn instance_map_with_evaluation>( + fn instance_map_with_evaluation>( cs: ConstraintSystemRef, t: &F, ) -> R1CSResult<(Vec, Vec, Vec, F, usize, usize)> { @@ -91,7 +117,7 @@ impl R1CStoQAP { } #[inline] - pub(crate) fn witness_map>( + fn witness_map>( prover: ConstraintSystemRef, ) -> R1CSResult> { let matrices = prover.to_matrices().unwrap(); @@ -158,4 +184,16 @@ impl R1CStoQAP { Ok(ab) } + + fn h_query_scalars>( + max_power: usize, + t: F, + zt: F, + delta_inverse: F, + ) -> Result, SynthesisError> { + let scalars = cfg_into_iter!(0..max_power) + .map(|i| zt * &delta_inverse * &t.pow([i as u64])) + .collect::>(); + Ok(scalars) + } }