Skip to content

Commit

Permalink
feat: allow using custom R1CS to QAP reductions (#34)
Browse files Browse the repository at this point in the history
Co-authored-by: Kobi Gurkan <kobigurk@gmail.com>
Co-authored-by: Pratyush Mishra <pratyushmishra@berkeley.edu>
  • Loading branch information
3 people authored Aug 10, 2021
1 parent 96ea457 commit d86285f
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 22 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### Features

- [\#34](https://github.com/arkworks-rs/groth16/pull/34) Allow specifying custom R1CS to QAP reductions.

### Improvements

### Bug fixes
Expand All @@ -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.

Expand Down
62 changes: 54 additions & 8 deletions src/generator.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<E, C, R>(circuit: C, rng: &mut R) -> R1CSResult<ProvingKey<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
{
generate_random_parameters_with_reduction::<E, C, R, LibsnarkReduction>(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<E, C, R, QAP>(
circuit: C,
rng: &mut R,
) -> R1CSResult<ProvingKey<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
QAP: R1CStoQAP,
{
let alpha = E::Fr::rand(rng);
let beta = E::Fr::rand(rng);
Expand All @@ -29,7 +48,7 @@ where
let g1_generator = E::G1Projective::rand(rng);
let g2_generator = E::G2Projective::rand(rng);

generate_parameters::<E, C, R>(
generate_parameters_with_qap::<E, C, R, QAP>(
circuit,
alpha,
beta,
Expand All @@ -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<E, C, R>(
circuit: C,
alpha: E::Fr,
Expand All @@ -56,6 +75,35 @@ where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
{
generate_parameters_with_qap::<E, C, R, LibsnarkReduction>(
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<E, C, R, 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<ProvingKey<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
QAP: R1CStoQAP,
{
type D<F> = GeneralEvaluationDomain<F>;

Expand Down Expand Up @@ -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::<E::Fr, D<E::Fr>>(cs, &t)?;
QAP::instance_map_with_evaluation::<E::Fr, D<E::Fr>>(cs, &t)?;
end_timer!(reduction_time);

// Compute query densities
Expand Down Expand Up @@ -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::<Vec<_>>(),
&QAP::h_query_scalars::<_, D<E::Fr>>(m_raw - 1, t, zt, delta_inverse)?,
);

end_timer!(h_time);
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
66 changes: 59 additions & 7 deletions src/prover.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<E, C, R>(
circuit: C,
pk: &ProvingKey<E>,
Expand All @@ -23,25 +25,58 @@ where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
{
create_random_proof_with_reduction::<E, C, R, LibsnarkReduction>(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<E, C, R, QAP>(
circuit: C,
pk: &ProvingKey<E>,
rng: &mut R,
) -> R1CSResult<Proof<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
R: Rng,
QAP: R1CStoQAP,
{
let r = E::Fr::rand(rng);
let s = E::Fr::rand(rng);

create_proof::<E, C>(circuit, pk, r, s)
create_proof_with_reduction::<E, C, QAP>(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<E, C>(circuit: C, pk: &ProvingKey<E>) -> R1CSResult<Proof<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
{
create_proof::<E, C>(circuit, pk, E::Fr::zero(), E::Fr::zero())
create_proof_with_reduction_no_zk::<E, C, LibsnarkReduction>(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<E, C, QAP>(
circuit: C,
pk: &ProvingKey<E>,
) -> R1CSResult<Proof<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
QAP: R1CStoQAP,
{
create_proof_with_reduction::<E, C, QAP>(circuit, pk, E::Fr::zero(), E::Fr::zero())
}

#[inline]
/// Create a Groth16 proof using randomness `r` and `s`.
pub fn create_proof<E, C>(
circuit: C,
pk: &ProvingKey<E>,
Expand All @@ -51,6 +86,23 @@ pub fn create_proof<E, C>(
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
{
create_proof_with_reduction::<E, C, LibsnarkReduction>(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<E, C, QAP>(
circuit: C,
pk: &ProvingKey<E>,
r: E::Fr,
s: E::Fr,
) -> R1CSResult<Proof<E>>
where
E: PairingEngine,
C: ConstraintSynthesizer<E::Fr>,
QAP: R1CStoQAP,
{
type D<F> = GeneralEvaluationDomain<F>;

Expand All @@ -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::<E::Fr, D<E::Fr>>(cs.clone())?;
let h = QAP::witness_map::<E::Fr, D<E::Fr>>(cs.clone())?;
end_timer!(witness_map_time);
let h_assignment = cfg_into_iter!(h).map(|s| s.into()).collect::<Vec<_>>();
let c_acc_time = start_timer!(|| "Compute C");
Expand Down
48 changes: 43 additions & 5 deletions src/r1cs_to_qap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<F: PrimeField, D: EvaluationDomain<F>>(
cs: ConstraintSystemRef<F>,
t: &F,
) -> Result<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize), SynthesisError>;

/// Computes a QAP witness corresponding to the R1CS witness defined by `cs`.
fn witness_map<F: PrimeField, D: EvaluationDomain<F>>(
prover: ConstraintSystemRef<F>,
) -> Result<Vec<F>, 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<F: PrimeField, D: EvaluationDomain<F>>(
max_power: usize,
t: F,
zt: F,
delta_inverse: F,
) -> Result<Vec<F>, 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<F: PrimeField, D: EvaluationDomain<F>>(
fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
cs: ConstraintSystemRef<F>,
t: &F,
) -> R1CSResult<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize)> {
Expand Down Expand Up @@ -91,7 +117,7 @@ impl R1CStoQAP {
}

#[inline]
pub(crate) fn witness_map<F: PrimeField, D: EvaluationDomain<F>>(
fn witness_map<F: PrimeField, D: EvaluationDomain<F>>(
prover: ConstraintSystemRef<F>,
) -> R1CSResult<Vec<F>> {
let matrices = prover.to_matrices().unwrap();
Expand Down Expand Up @@ -158,4 +184,16 @@ impl R1CStoQAP {

Ok(ab)
}

fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(
max_power: usize,
t: F,
zt: F,
delta_inverse: F,
) -> Result<Vec<F>, SynthesisError> {
let scalars = cfg_into_iter!(0..max_power)
.map(|i| zt * &delta_inverse * &t.pow([i as u64]))
.collect::<Vec<_>>();
Ok(scalars)
}
}

0 comments on commit d86285f

Please sign in to comment.