-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat/circom prover removes ark-circom dep. (#312)
* migrate circom-compact * test: complete testing * chore: adding some notes * chore: update deps. * move to_ethereum_proof/inputs to mopro-ffi * remove unnecessary export * chore: typo fix
- Loading branch information
Showing
19 changed files
with
2,377 additions
and
447 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
mod zkey; | ||
use ark_ec::pairing::Pairing; | ||
pub use zkey::{read_proving_key, read_zkey, FieldSerialization, ZkeyHeaderReader}; | ||
|
||
mod qap; | ||
pub use qap::CircomReduction; | ||
|
||
mod circuit; | ||
pub use circuit::CircomCircuit; | ||
|
||
mod r1cs_reader; | ||
pub use r1cs_reader::R1CSFile; | ||
|
||
pub type Constraints<E> = (ConstraintVec<E>, ConstraintVec<E>, ConstraintVec<E>); | ||
pub type ConstraintVec<E> = Vec<(usize, <E as Pairing>::ScalarField)>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// This file is copied from https://github.dev/zkmopro/circom-compat/tree/wasm-delete | ||
|
||
use anyhow::Result; | ||
use ark_ec::pairing::Pairing; | ||
use ark_relations::r1cs::{ | ||
ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable, | ||
}; | ||
|
||
use super::r1cs_reader::R1CS; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct CircomCircuit<E: Pairing> { | ||
pub r1cs: R1CS<E>, | ||
pub witness: Option<Vec<E::ScalarField>>, | ||
} | ||
|
||
impl<E: Pairing> CircomCircuit<E> { | ||
pub fn get_public_inputs(&self) -> Option<Vec<E::ScalarField>> { | ||
match &self.witness { | ||
None => None, | ||
Some(w) => match &self.r1cs.wire_mapping { | ||
None => Some(w[1..self.r1cs.num_inputs].to_vec()), | ||
Some(m) => Some(m[1..self.r1cs.num_inputs].iter().map(|i| w[*i]).collect()), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl<E: Pairing> ConstraintSynthesizer<E::ScalarField> for CircomCircuit<E> { | ||
fn generate_constraints( | ||
self, | ||
cs: ConstraintSystemRef<E::ScalarField>, | ||
) -> Result<(), SynthesisError> { | ||
let witness = &self.witness; | ||
let wire_mapping = &self.r1cs.wire_mapping; | ||
|
||
// Start from 1 because Arkworks implicitly allocates One for the first input | ||
for i in 1..self.r1cs.num_inputs { | ||
cs.new_input_variable(|| { | ||
Ok(match witness { | ||
None => E::ScalarField::from(1u32), | ||
Some(w) => match wire_mapping { | ||
Some(m) => w[m[i]], | ||
None => w[i], | ||
}, | ||
}) | ||
})?; | ||
} | ||
|
||
for i in 0..self.r1cs.num_aux { | ||
cs.new_witness_variable(|| { | ||
Ok(match witness { | ||
None => E::ScalarField::from(1u32), | ||
Some(w) => match wire_mapping { | ||
Some(m) => w[m[i + self.r1cs.num_inputs]], | ||
None => w[i + self.r1cs.num_inputs], | ||
}, | ||
}) | ||
})?; | ||
} | ||
|
||
let make_index = |index| { | ||
if index < self.r1cs.num_inputs { | ||
Variable::Instance(index) | ||
} else { | ||
Variable::Witness(index - self.r1cs.num_inputs) | ||
} | ||
}; | ||
let make_lc = |lc_data: &[(usize, E::ScalarField)]| { | ||
lc_data.iter().fold( | ||
LinearCombination::<E::ScalarField>::zero(), | ||
|lc: LinearCombination<E::ScalarField>, (index, coeff)| { | ||
lc + (*coeff, make_index(*index)) | ||
}, | ||
) | ||
}; | ||
|
||
for constraint in &self.r1cs.constraints { | ||
cs.enforce_constraint( | ||
make_lc(&constraint.0), | ||
make_lc(&constraint.1), | ||
make_lc(&constraint.2), | ||
)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// This file is copied from https://github.dev/zkmopro/circom-compat/tree/wasm-delete | ||
|
||
use ark_ff::PrimeField; | ||
use ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CSToQAP}; | ||
use ark_poly::EvaluationDomain; | ||
use ark_relations::r1cs::{ConstraintMatrices, ConstraintSystemRef, SynthesisError}; | ||
use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec}; | ||
|
||
// We need this for `cfg_iter_mut`. | ||
#[cfg(feature = "parallel")] | ||
use rayon::prelude::*; | ||
|
||
/// Implements the witness map used by snarkjs. The arkworks witness map calculates the | ||
/// coefficients of H through computing (AB-C)/Z in the evaluation domain and going back to the | ||
/// coefficients domain. snarkjs instead precomputes the Lagrange form of the powers of tau bases | ||
/// in a domain twice as large and the witness map is computed as the odd coefficients of (AB-C) | ||
/// in that domain. This serves as HZ when computing the C proof element. | ||
pub struct CircomReduction; | ||
|
||
impl R1CSToQAP for CircomReduction { | ||
#[allow(clippy::type_complexity)] | ||
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> { | ||
LibsnarkReduction::instance_map_with_evaluation::<F, D>(cs, t) | ||
} | ||
|
||
fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>( | ||
matrices: &ConstraintMatrices<F>, | ||
num_inputs: usize, | ||
num_constraints: usize, | ||
full_assignment: &[F], | ||
) -> Result<Vec<F>, SynthesisError> { | ||
let zero = F::zero(); | ||
let domain = | ||
D::new(num_constraints + num_inputs).ok_or(SynthesisError::PolynomialDegreeTooLarge)?; | ||
let domain_size = domain.size(); | ||
|
||
let mut a = vec![zero; domain_size]; | ||
let mut b = vec![zero; domain_size]; | ||
|
||
cfg_iter_mut!(a[..num_constraints]) | ||
.zip(cfg_iter_mut!(b[..num_constraints])) | ||
.zip(cfg_iter!(&matrices.a)) | ||
.zip(cfg_iter!(&matrices.b)) | ||
.for_each(|(((a, b), at_i), bt_i)| { | ||
*a = evaluate_constraint(at_i, full_assignment); | ||
*b = evaluate_constraint(bt_i, full_assignment); | ||
}); | ||
|
||
{ | ||
let start = num_constraints; | ||
let end = start + num_inputs; | ||
a[start..end].clone_from_slice(&full_assignment[..num_inputs]); | ||
} | ||
|
||
let mut c = vec![zero; domain_size]; | ||
cfg_iter_mut!(c[..num_constraints]) | ||
.zip(&a) | ||
.zip(&b) | ||
.for_each(|((c_i, &a), &b)| { | ||
*c_i = a * b; | ||
}); | ||
|
||
domain.ifft_in_place(&mut a); | ||
domain.ifft_in_place(&mut b); | ||
|
||
let root_of_unity = { | ||
let domain_size_double = 2 * domain_size; | ||
let domain_double = | ||
D::new(domain_size_double).ok_or(SynthesisError::PolynomialDegreeTooLarge)?; | ||
domain_double.element(1) | ||
}; | ||
D::distribute_powers_and_mul_by_const(&mut a, root_of_unity, F::one()); | ||
D::distribute_powers_and_mul_by_const(&mut b, root_of_unity, F::one()); | ||
|
||
domain.fft_in_place(&mut a); | ||
domain.fft_in_place(&mut b); | ||
|
||
let mut ab = domain.mul_polynomials_in_evaluation_domain(&a, &b); | ||
drop(a); | ||
drop(b); | ||
|
||
domain.ifft_in_place(&mut c); | ||
D::distribute_powers_and_mul_by_const(&mut c, root_of_unity, F::one()); | ||
domain.fft_in_place(&mut c); | ||
|
||
cfg_iter_mut!(ab) | ||
.zip(c) | ||
.for_each(|(ab_i, c_i)| *ab_i -= &c_i); | ||
|
||
Ok(ab) | ||
} | ||
|
||
fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>( | ||
max_power: usize, | ||
t: F, | ||
_: F, | ||
delta_inverse: F, | ||
) -> Result<Vec<F>, SynthesisError> { | ||
// the usual H query has domain-1 powers. Z has domain powers. So HZ has 2*domain-1 powers. | ||
let mut scalars = cfg_into_iter!(0..2 * max_power + 1) | ||
.map(|i| delta_inverse * t.pow([i as u64])) | ||
.collect::<Vec<_>>(); | ||
let domain_size = scalars.len(); | ||
let domain = D::new(domain_size).ok_or(SynthesisError::PolynomialDegreeTooLarge)?; | ||
// generate the lagrange coefficients | ||
domain.ifft_in_place(&mut scalars); | ||
Ok(cfg_into_iter!(scalars).skip(1).step_by(2).collect()) | ||
} | ||
} |
Oops, something went wrong.