diff --git a/Cargo.toml b/Cargo.toml index fba074c1d..9f9254cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "examples/merkle-tree-cli", "examples/prove-miden", "winterfell_adapter"] + +members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "provers/groth16/arkworks-adapter", "examples/merkle-tree-cli", "examples/prove-miden", "winterfell_adapter"] exclude = ["ensure-no_std"] resolver = "2" diff --git a/provers/groth16/README.md b/provers/groth16/README.md index 101de44b3..dae3aeae1 100644 --- a/provers/groth16/README.md +++ b/provers/groth16/README.md @@ -1,3 +1,3 @@ # Lambdaworks Groth16 Prover -An incomplete and unoptimized implementation of the [Groth16](https://eprint.iacr.org/2016/260) protocol. +An under-optimized implementation of [Groth16](https://eprint.iacr.org/2016/260) protocol. diff --git a/provers/groth16/arkworks-adapter/Cargo.toml b/provers/groth16/arkworks-adapter/Cargo.toml new file mode 100644 index 000000000..637070bdb --- /dev/null +++ b/provers/groth16/arkworks-adapter/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "arkworks_adapter" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lambdaworks-math = { path = "../../../math" } +lambdaworks-groth16 = { path = "../" } +ark-r1cs-std = { version = "^0.3.1"} +ark-bls12-381 = { version = "0.4.0"} +ark-ff = { version = "^0.4.2" } +ark-relations = { version = "^0.4.0" } +ark-serialize = {version = "0.4.2"} +num-bigint = { version = "0.4", default-features = false } +rand = "0.8.5" diff --git a/provers/groth16/arkworks-adapter/README.md b/provers/groth16/arkworks-adapter/README.md new file mode 100644 index 000000000..442122c30 --- /dev/null +++ b/provers/groth16/arkworks-adapter/README.md @@ -0,0 +1,84 @@ +# Arkworks Adapter for Lambdaworks Groth16 Backend + +This crate enables circuits written in Arkworks to be proven / verified via Lambdaworks Groth16 backend. + +Crate exposes [to_lambda](./src/lib.rs#to_lambda) function for ConstraintSystemRef type of Arkworks, which is expected to carry all constraints, witnesses, and public variable assignments: + +```rust +pub fn to_lambda(cs: &ConstraintSystemRef) -> (QuadraticArithmeticProgram, Vec) +``` + +It returns a Lambdaworks-compatible QAP struct alongside variable assignments. Please note that public variable assignments are bundled with witnesses, and this vector of field elements is called **witness** alltogether. + +```rust +let (qap, w) = to_lambda(&cs); +``` + +After this point, typical steps of Groth16 can be performed using Lamdaworks: setup, prove, verify + +```rust +let (pk, vk) = setup(&qap); + +let proof = Prover::prove(&w, &qap, &pk); + +let public_inputs = &w[..qap.num_of_public_inputs]; +let accept = verify(&vk, &proof, public_inputs); + +assert!(accept); +``` + +## Full Example + +A linear exponentiation example on BLS12-381 can be found here. +Please check [integration_tests.rs](./src/integration_tests.rs) for more examples. + +```rust + +use crate::to_lambda; +use ark_bls12_381::Fr; +use ark_relations::{lc, r1cs::ConstraintSystem, r1cs::Variable}; +use lambdaworks_groth16::{setup, verify, Prover}; +use rand::Rng; + +// ... +// ... + +let mut rng = rand::thread_rng(); +let x = rng.gen::(); +let exp = rng.gen::(); + +// Define the circuit using Arkworks + +let cs = ConstraintSystem::::new_ref(); + +let x = Fr::from(x); +let mut _x = cs.new_witness_variable(|| Ok(x)).unwrap(); + +let mut acc = Fr::from(x); +let mut _acc = cs.new_witness_variable(|| Ok(x)).unwrap(); + +for _ in 0..exp - 1 { + acc *= x; + let _new_acc = cs.new_witness_variable(|| Ok(acc)).unwrap(); + cs.enforce_constraint(lc!() + _acc, lc!() + _x, lc!() + _new_acc) + .unwrap(); + _acc = _new_acc; +} + +let _out = cs.new_input_variable(|| Ok(acc)).unwrap(); +cs.enforce_constraint(lc!() + _out, lc!() + Variable::One, lc!() + _acc) + .unwrap(); + +// Make Lambdaworks-compatible +let (qap, w) = to_lambda(&cs); + +// Use Lambdaworks Groth16 backend + +let (pk, vk) = setup(&qap); + +let proof = Prover::prove(&w, &qap, &pk); + +let public_inputs = &w[..qap.num_of_public_inputs]; +let accept = verify(&vk, &proof, public_inputs); +assert!(accept); +``` diff --git a/provers/groth16/arkworks-adapter/src/integration_tests.rs b/provers/groth16/arkworks-adapter/src/integration_tests.rs new file mode 100644 index 000000000..d03f5e79d --- /dev/null +++ b/provers/groth16/arkworks-adapter/src/integration_tests.rs @@ -0,0 +1,195 @@ +use crate::arkworks_cs_to_lambda_cs; +use ark_bls12_381::Fr; +use ark_relations::{lc, r1cs::ConstraintSystem, r1cs::Variable}; +use lambdaworks_groth16::{setup, verify, Prover, QuadraticArithmeticProgram}; +use rand::Rng; + +#[test] +fn pinocchio_paper_example() { + /* + pub inp a, b, c, d + pub out result + sig e + + c * d = e + (a + b) + e = result + */ + let cs = ConstraintSystem::::new_ref(); + + let _a = Fr::from(1555); + let _b = Fr::from(25555); + let _c = Fr::from(35555); + let _d = Fr::from(45555); + + let a = cs.new_input_variable(|| Ok(_a)).unwrap(); + let b = cs.new_input_variable(|| Ok(_b)).unwrap(); + let c = cs.new_input_variable(|| Ok(_c)).unwrap(); + let d = cs.new_input_variable(|| Ok(_d)).unwrap(); + + let e = cs.new_witness_variable(|| Ok(_c * _d)).unwrap(); + cs.enforce_constraint(lc!() + c, lc!() + d, lc!() + e) + .unwrap(); + + let result = cs.new_input_variable(|| Ok(_c * _d * (_a + _b))).unwrap(); + + cs.enforce_constraint(lc!() + a + b, lc!() + e, lc!() + result) + .unwrap(); + + let lambda_cs = arkworks_cs_to_lambda_cs(&cs); + + let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); + + let (pk, vk) = setup(&qap); + + let accept = verify( + &vk, + &Prover::prove(&lambda_cs.witness, &qap, &pk), + &lambda_cs.witness[..qap.num_of_public_inputs], + ); + assert!(accept); +} + +#[test] +fn vitalik_example() { + /* + pub out ~out + sig x, sym_1, y, sym_2 + + x * x = sym_1; + sym_1 * x = y; + (y + x) * 1 = sym_2 + (sym_2 + 5) * 1 = ~out + */ + let cs = ConstraintSystem::::new_ref(); + + //["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"] + let _x = Fr::from(3); + let _sym_1 = Fr::from(9); + let _y = Fr::from(27); + let _sym_2 = Fr::from(30); + + let _out = Fr::from(35); + + let x = cs.new_witness_variable(|| Ok(_x)).unwrap(); + let sym_1 = cs.new_witness_variable(|| Ok(_sym_1)).unwrap(); + let y = cs.new_witness_variable(|| Ok(_y)).unwrap(); + let sym_2 = cs.new_witness_variable(|| Ok(_sym_2)).unwrap(); + + let out = cs.new_input_variable(|| Ok(_out)).unwrap(); + + cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1) + .unwrap(); + cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y) + .unwrap(); + cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2) + .unwrap(); + cs.enforce_constraint( + lc!() + sym_2 + (Fr::from(5), Variable::One), + lc!() + Variable::One, + lc!() + out, + ) + .unwrap(); + + let lambda_cs = arkworks_cs_to_lambda_cs(&cs); + + let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); + + let (pk, vk) = setup(&qap); + + let accept = verify( + &vk, + &Prover::prove(&lambda_cs.witness, &qap, &pk), + &lambda_cs.witness[..qap.num_of_public_inputs], + ); + assert!(accept); +} + +#[test] +fn failing_vitalik() { + // Same circuit as vitalik_example, but with an incorrect witness assignment. + let cs = ConstraintSystem::::new_ref(); + + //["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"] + let _x = Fr::from(3); + let _sym_1 = Fr::from(10); // should be have been 9 + let _y = Fr::from(27); + let _sym_2 = Fr::from(30); + + let _out = Fr::from(35); + + let x = cs.new_witness_variable(|| Ok(_x)).unwrap(); + let sym_1 = cs.new_witness_variable(|| Ok(_sym_1)).unwrap(); + let y = cs.new_witness_variable(|| Ok(_y)).unwrap(); + let sym_2 = cs.new_witness_variable(|| Ok(_sym_2)).unwrap(); + + let out = cs.new_input_variable(|| Ok(_out)).unwrap(); + + cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1) + .unwrap(); + cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y) + .unwrap(); + cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2) + .unwrap(); + cs.enforce_constraint( + lc!() + sym_2 + (Fr::from(5), Variable::One), + lc!() + Variable::One, + lc!() + out, + ) + .unwrap(); + + let lambda_cs = arkworks_cs_to_lambda_cs(&cs); + + let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); + + let (pk, vk) = setup(&qap); + + let accept = verify( + &vk, + &Prover::prove(&lambda_cs.witness, &qap, &pk), + &lambda_cs.witness[..qap.num_of_public_inputs], + ); + assert!(!accept); +} + +#[test] +fn exponentiation_example() { + /* + Generates a "linear exponentiation" circuit with a random base and a random exponent. + Only the output ~out is public input. + */ + let cs = ConstraintSystem::::new_ref(); + + let mut rng = rand::thread_rng(); + let x = rng.gen::(); + let exp = rng.gen::(); // Bigger data types take too much time for a test + + let x = Fr::from(x); + let mut _x = cs.new_witness_variable(|| Ok(x)).unwrap(); + + let mut acc = Fr::from(x); + let mut _acc = cs.new_witness_variable(|| Ok(x)).unwrap(); + + for _ in 0..exp - 1 { + acc *= x; + let _new_acc = cs.new_witness_variable(|| Ok(acc)).unwrap(); + cs.enforce_constraint(lc!() + _acc, lc!() + _x, lc!() + _new_acc) + .unwrap(); + _acc = _new_acc; + } + + let _out = cs.new_input_variable(|| Ok(acc)).unwrap(); + cs.enforce_constraint(lc!() + _out, lc!() + Variable::One, lc!() + _acc) + .unwrap(); + + let lambda_cs = arkworks_cs_to_lambda_cs(&cs); + + let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); + + let (pk, vk) = setup(&qap); + + let proof = Prover::prove(&lambda_cs.witness, &qap, &pk); + + let public_inputs = &lambda_cs.witness[..qap.num_of_public_inputs]; + let accept = verify(&vk, &proof, public_inputs); + assert!(accept); +} diff --git a/provers/groth16/arkworks-adapter/src/lib.rs b/provers/groth16/arkworks-adapter/src/lib.rs new file mode 100644 index 000000000..f0403ef20 --- /dev/null +++ b/provers/groth16/arkworks-adapter/src/lib.rs @@ -0,0 +1,107 @@ +#[cfg(test)] +mod integration_tests; + +use ark_ff::PrimeField; +use ark_relations::r1cs::{ConstraintSystemRef, Field}; +use lambdaworks_groth16::{common::*, r1cs::R1CS, ConstraintSystem}; +use lambdaworks_math::traits::ByteConversion; + +use std::ops::Deref; + +/// Accepts an Arkworks circuit as a ConstraintSystem reference, and creates a +/// Lambdaworks ConstraintSystem, which can then be used with the Lambdaworks +/// Groth16 backend for setup, proving, and verification. +pub fn arkworks_cs_to_lambda_cs( + cs: &ConstraintSystemRef, +) -> ConstraintSystem { + ConstraintSystem { + constraints: r1cs_from_arkworks_cs(cs), + witness: extract_witness_from_arkworks_cs(cs), + } +} + +#[inline] +fn r1cs_from_arkworks_cs(cs: &ConstraintSystemRef) -> R1CS { + cs.inline_all_lcs(); + + let r1cs_matrices = cs.to_matrices().unwrap(); + let num_pub_vars = cs.num_instance_variables(); + let total_variables = cs.num_witness_variables() + num_pub_vars; + + R1CS::from_matrices( + ark_to_lambda_matrix(&r1cs_matrices.a, total_variables), + ark_to_lambda_matrix(&r1cs_matrices.b, total_variables), + ark_to_lambda_matrix(&r1cs_matrices.c, total_variables), + num_pub_vars, + ) +} + +#[inline] +fn ark_to_lambda_matrix( + m: &[Vec<(F, usize)>], + total_variables: usize, +) -> Vec> { + sparse_matrix_to_dense(&arkworks_matrix_fps_to_fr_elements(m), total_variables) +} + +#[inline] +fn arkworks_matrix_fps_to_fr_elements( + m: &[Vec<(F, usize)>], +) -> Vec> { + m.iter() + .map(|x| { + x.iter() + .map(|(x, y)| (ark_fr_to_fr_element(x), *y)) + .collect() + }) + .collect() +} + +#[inline] +fn extract_witness_from_arkworks_cs(cs: &ConstraintSystemRef) -> Vec { + let binding = cs.borrow().unwrap(); + let borrowed_cs_ref = binding.deref(); + + // Place public variables first, then witness assignments. + // That's how Lambdaworks Groth16 expects the witness vector. + let mut witness = vec![]; + witness.extend( + borrowed_cs_ref + .instance_assignment + .iter() + .map(ark_fr_to_fr_element), + ); + witness.extend( + borrowed_cs_ref + .witness_assignment + .iter() + .map(ark_fr_to_fr_element), + ); + witness +} + +#[inline] +fn ark_fr_to_fr_element(ark_fq: &F) -> FrElement { + let mut buff = Vec::::new(); + ark_fq.serialize_compressed(&mut buff).unwrap(); + FrElement::from_bytes_le(&buff).unwrap() +} + +#[inline] +fn sparse_matrix_to_dense( + m: &[Vec<(FrElement, usize)>], + total_variables: usize, +) -> Vec> { + m.iter() + .map(|row| sparse_row_to_dense(row, total_variables)) + .collect() +} + +#[inline] +fn sparse_row_to_dense(row: &[(FrElement, usize)], total_variables: usize) -> Vec { + let mut dense_row = vec![FrElement::from(0); total_variables]; + row.iter().for_each(|e| { + dense_row[e.1] = e.0.clone(); + }); + dense_row +} diff --git a/provers/groth16/src/common.rs b/provers/groth16/src/common.rs index c4ede834e..c31d2fe39 100644 --- a/provers/groth16/src/common.rs +++ b/provers/groth16/src/common.rs @@ -1,10 +1,8 @@ use lambdaworks_math::{ elliptic_curve::{ short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement as FE, FrField as FrF}, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, + curve::BLS12381Curve, default_types::FrElement as FE, default_types::FrField as FrF, + pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, }, traits::{IsEllipticCurve, IsPairing}, }, diff --git a/provers/groth16/src/lib.rs b/provers/groth16/src/lib.rs index 5ea5579b7..1dbdfddfe 100644 --- a/provers/groth16/src/lib.rs +++ b/provers/groth16/src/lib.rs @@ -1,6 +1,6 @@ pub mod common; pub mod qap; -pub mod test_circuits; +pub mod r1cs; mod prover; mod setup; @@ -8,7 +8,6 @@ mod verifier; pub use prover::{Proof, Prover}; pub use qap::QuadraticArithmeticProgram; -pub use setup::{setup, ProvingKey, VerifyingKey}; +pub use r1cs::*; +pub use setup::*; pub use verifier::verify; - -pub use test_circuits::*; diff --git a/provers/groth16/src/qap.rs b/provers/groth16/src/qap.rs index d2433c4f5..94a4c6f17 100644 --- a/provers/groth16/src/qap.rs +++ b/provers/groth16/src/qap.rs @@ -1,68 +1,27 @@ use lambdaworks_math::polynomial::Polynomial; -use crate::common::*; +use crate::{common::*, r1cs::R1CS}; #[derive(Debug)] pub struct QuadraticArithmeticProgram { pub num_of_public_inputs: usize, + pub num_of_gates: usize, pub l: Vec>, pub r: Vec>, pub o: Vec>, } impl QuadraticArithmeticProgram { - pub fn from_variable_matrices( - num_of_public_inputs: usize, - l: &[Vec], - r: &[Vec], - o: &[Vec], - ) -> Self { - let num_of_total_inputs = l.len(); - assert_eq!(num_of_total_inputs, r.len()); - assert_eq!(num_of_total_inputs, o.len()); - assert!(num_of_total_inputs > 0); - assert!(num_of_public_inputs <= num_of_total_inputs); - - let num_of_gates = l[0].len(); - let pad_zeroes = num_of_gates.next_power_of_two() - num_of_gates; - let l = Self::apply_padding(l, pad_zeroes); - let r = Self::apply_padding(r, pad_zeroes); - let o = Self::apply_padding(o, pad_zeroes); - - Self { - num_of_public_inputs, - l: Self::build_variable_polynomials(&l), - r: Self::build_variable_polynomials(&r), - o: Self::build_variable_polynomials(&o), - } - } - - pub fn num_of_gates(&self) -> usize { - self.l[0].degree() + 1 - } - - pub fn num_of_private_inputs(&self) -> usize { - self.l.len() - self.num_of_public_inputs - } - - pub fn num_of_total_inputs(&self) -> usize { - self.l.len() - } - pub fn calculate_h_coefficients(&self, w: &[FrElement]) -> Vec { let offset = &ORDER_R_MINUS_1_ROOT_UNITY; - let degree = self.num_of_gates() * 2; + let degree = self.num_of_gates * 2; let [l, r, o] = self.scale_and_accumulate_variable_polynomials(w, degree, offset); // TODO: Change to a vector of offsetted evaluations of x^N-1 - let mut t = Polynomial::evaluate_offset_fft( - &(Polynomial::new_monomial(FrElement::one(), self.num_of_gates()) - FrElement::one()), - 1, - Some(degree), - offset, - ) - .unwrap(); + let t_poly = + Polynomial::new_monomial(FrElement::one(), self.num_of_gates) - FrElement::one(); + let mut t = Polynomial::evaluate_offset_fft(&t_poly, 1, Some(degree), offset).unwrap(); FrElement::inplace_batch_inverse(&mut t).unwrap(); let h_evaluated = l @@ -79,25 +38,6 @@ impl QuadraticArithmeticProgram { .to_vec() } - fn apply_padding(columns: &[Vec], pad_zeroes: usize) -> Vec> { - let from_slice = vec![FrElement::zero(); pad_zeroes]; - columns - .iter() - .map(|column| { - let mut new_column = column.clone(); - new_column.extend_from_slice(&from_slice); - new_column - }) - .collect::>() - } - - fn build_variable_polynomials(from_matrix: &[Vec]) -> Vec> { - from_matrix - .iter() - .map(|row| Polynomial::interpolate_fft::(row).unwrap()) - .collect() - } - // Compute A.s by summing up polynomials A[0].s, A[1].s, ..., A[n].s // In other words, assign the witness coefficients / execution values // Similarly for B.s and C.s @@ -124,4 +64,54 @@ impl QuadraticArithmeticProgram { .unwrap() }) } + + pub fn num_of_private_inputs(&self) -> usize { + self.l.len() - self.num_of_public_inputs + } + + pub fn from_r1cs(r1cs: R1CS) -> QuadraticArithmeticProgram { + let num_gates = r1cs.number_of_constraints(); + let next_power_of_two = num_gates.next_power_of_two(); + let pad_zeroes = next_power_of_two - num_gates; + + let mut l: Vec> = vec![]; + let mut r: Vec> = vec![]; + let mut o: Vec> = vec![]; + for i in 0..r1cs.witness_size() { + let [l_poly, r_poly, o_poly] = + get_variable_lro_polynomials_from_r1cs(&r1cs, i, pad_zeroes); + l.push(l_poly); + r.push(r_poly); + o.push(o_poly); + } + + QuadraticArithmeticProgram { + l, + r, + o, + num_of_gates: next_power_of_two, + num_of_public_inputs: r1cs.number_of_inputs, + } + } +} + +#[inline] +fn get_variable_lro_polynomials_from_r1cs( + r1cs: &R1CS, + var_idx: usize, + pad_zeroes: usize, +) -> [Polynomial; 3] { + let cap = r1cs.number_of_constraints() + pad_zeroes; + let mut current_var_l = vec![FrElement::zero(); cap]; + let mut current_var_r = vec![FrElement::zero(); cap]; + let mut current_var_o = vec![FrElement::zero(); cap]; + + for (i, c) in r1cs.constraints.iter().enumerate() { + current_var_l[i] = c.a[var_idx].clone(); + current_var_r[i] = c.b[var_idx].clone(); + current_var_o[i] = c.c[var_idx].clone(); + } + + [current_var_l, current_var_r, current_var_o] + .map(|e| Polynomial::interpolate_fft::(&e).unwrap()) } diff --git a/provers/groth16/src/r1cs.rs b/provers/groth16/src/r1cs.rs new file mode 100644 index 000000000..c1a96d1bf --- /dev/null +++ b/provers/groth16/src/r1cs.rs @@ -0,0 +1,50 @@ +use crate::common::FrElement; +use lambdaworks_math::field::{element::FieldElement, traits::IsField}; + +// To be improved with a front-end implementation +// TODO: Use CS in Groth16 tests instead of a plain QAP +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ConstraintSystem { + pub constraints: R1CS, + pub witness: Vec>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Constraint { + pub a: Vec, + pub b: Vec, + pub c: Vec, +} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct R1CS { + pub constraints: Vec, + pub number_of_inputs: usize, +} + +impl R1CS { + pub fn from_matrices( + a: Vec>, + b: Vec>, + c: Vec>, + number_of_inputs: usize, + ) -> Self { + Self { + constraints: (0..a.len()) + .map(|i| Constraint { + a: a[i].clone(), + b: b[i].clone(), + c: c[i].clone(), + }) + .collect(), + number_of_inputs, + } + } + + pub fn number_of_constraints(&self) -> usize { + self.constraints.len() + } + + pub fn witness_size(&self) -> usize { + self.constraints[0].a.len() + } +} diff --git a/provers/groth16/src/setup.rs b/provers/groth16/src/setup.rs index f1611cc3c..4e941d68b 100644 --- a/provers/groth16/src/setup.rs +++ b/provers/groth16/src/setup.rs @@ -107,10 +107,10 @@ pub fn setup(qap: &QuadraticArithmeticProgram) -> (ProvingKey, VerifyingKey) { &core::iter::successors( // Start from delta^{-1} * t(τ) // Note that t(τ) = (τ^N - 1) because our domain is roots of unity - Some(&delta_inv * (&tw.tau.pow(qap.num_of_gates()) - FrElement::one())), + Some(&delta_inv * (&tw.tau.pow(qap.num_of_gates) - FrElement::one())), |prev| Some(prev * &tw.tau), ) - .take(qap.num_of_gates() * 2) + .take(qap.num_of_gates * 2) .collect::>(), &g1, ), diff --git a/provers/groth16/tests/groth16.rs b/provers/groth16/tests/groth16.rs index c1b4cc327..4df7d1f93 100644 --- a/provers/groth16/tests/groth16.rs +++ b/provers/groth16/tests/groth16.rs @@ -1,41 +1,32 @@ -use lambdaworks_groth16::{common::*, setup, test_circuits::*, verify, Proof, Prover}; +use lambdaworks_groth16::{common::*, setup, verify, Proof, Prover}; -#[test] -fn vitalik_1() { - let qap = vitalik_qap(); // x^3 + x + 5 = 35 - - let (pk, vk) = setup(&qap); - - let w = ["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"] // x = 3 - .map(FrElement::from_hex_unchecked) - .to_vec(); - - let serialized_proof = Prover::prove(&w, &qap, &pk).serialize(); - let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - - let accept = verify(&vk, &deserialized_proof, &w[..qap.num_of_public_inputs]); - assert!(accept); -} +mod test_circuits; +use test_circuits::*; #[test] -fn vitalik_2() { - let qap = vitalik_qap(); // x^3 + x + 5 = 35 +fn vitalik() { + let qap = test_circuits::vitalik_qap(); // x^3 + x + 5 = 35 let (pk, vk) = setup(&qap); - let w = ["0x1", "0x1", "0x7", "0x1", "0x1", "0x2"] // x = 1 - .map(FrElement::from_hex_unchecked) - .to_vec(); + for w in [ + ["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"], + ["0x1", "0x1", "0x7", "0x1", "0x1", "0x2"], + ] { + let w = w // x = 3 + .map(FrElement::from_hex_unchecked) + .to_vec(); - let serialized_proof = Prover::prove(&w, &qap, &pk).serialize(); - let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); + let serialized_proof = Prover::prove(&w, &qap, &pk).serialize(); + let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - let accept = verify(&vk, &deserialized_proof, &w[..qap.num_of_public_inputs]); - assert!(accept); + let accept = verify(&vk, &deserialized_proof, &w[..qap.num_of_public_inputs]); + assert!(accept); + } } #[test] -fn qap_2() { +fn example() { let qap = test_qap_2(); let (pk, vk) = setup(&qap); diff --git a/provers/groth16/src/test_circuits/mod.rs b/provers/groth16/tests/test_circuits/mod.rs similarity index 81% rename from provers/groth16/src/test_circuits/mod.rs rename to provers/groth16/tests/test_circuits/mod.rs index 41e84e24f..bbaf4f3f4 100644 --- a/provers/groth16/src/test_circuits/mod.rs +++ b/provers/groth16/tests/test_circuits/mod.rs @@ -1,22 +1,26 @@ -use crate::{common::*, QuadraticArithmeticProgram}; +use lambdaworks_groth16::{common::*, QuadraticArithmeticProgram}; + +mod utils; +use utils::*; /* Represents x^3 + x + 5 = 35, based on https://vitalik.ca/general/2016/12/10/qap.html - sym_1 = x * x - y = sym_1 * x - sym_2 = y + x - ~out = sym_2 + 5 + x * x = sym_1; + sym_1 * x = y; + (y + x) * 1 = sym_2 + (sym_2 + 5) * 1 = ~out */ +#[cfg(test)] pub fn vitalik_qap() -> QuadraticArithmeticProgram { let num_of_public_inputs = 1; let [l, r, o] = [ [ - ["0", "0", "0", "5"], - ["1", "0", "1", "0"], - ["0", "0", "0", "0"], - ["0", "1", "0", "0"], - ["0", "0", "1", "0"], - ["0", "0", "0", "1"], + ["0", "0", "0", "5"], // 1 + ["1", "0", "1", "0"], // x + ["0", "0", "0", "0"], // ~out + ["0", "1", "0", "0"], // sym_1 + ["0", "0", "1", "0"], // y + ["0", "0", "0", "1"], // sym_2 ], [ ["0", "0", "1", "1"], @@ -36,7 +40,7 @@ pub fn vitalik_qap() -> QuadraticArithmeticProgram { ], ] .map(|matrix| matrix.map(|row| row.map(FrElement::from_hex_unchecked).to_vec())); - QuadraticArithmeticProgram::from_variable_matrices(num_of_public_inputs, &l, &r, &o) + qap_from_variable_matrices(num_of_public_inputs, &l, &r, &o) } /* @@ -50,7 +54,8 @@ Represents x^2 = 25 or y^2 = 9 sym_4 = sym_2 - 9 ~out = sym_3 * sym_4 -> needs to be zero -*/// +*/ +#[cfg(test)] pub fn test_qap_2() -> QuadraticArithmeticProgram { let num_of_public_inputs = 2; let [l, r, o] = [ @@ -97,5 +102,5 @@ pub fn test_qap_2() -> QuadraticArithmeticProgram { .to_vec() }) }); - QuadraticArithmeticProgram::from_variable_matrices(num_of_public_inputs, &l, &r, &o) + qap_from_variable_matrices(num_of_public_inputs, &l, &r, &o) } diff --git a/provers/groth16/tests/test_circuits/utils.rs b/provers/groth16/tests/test_circuits/utils.rs new file mode 100644 index 000000000..e618eccf4 --- /dev/null +++ b/provers/groth16/tests/test_circuits/utils.rs @@ -0,0 +1,48 @@ +use lambdaworks_groth16::{common::*, QuadraticArithmeticProgram}; +use lambdaworks_math::polynomial::Polynomial; + +#[cfg(test)] +pub fn qap_from_variable_matrices( + num_of_public_inputs: usize, + l: &[Vec], + r: &[Vec], + o: &[Vec], +) -> QuadraticArithmeticProgram { + let num_of_total_inputs = l.len(); + assert_eq!(num_of_total_inputs, r.len()); + assert_eq!(num_of_total_inputs, o.len()); + assert!(num_of_total_inputs > 0); + assert!(num_of_public_inputs <= num_of_total_inputs); + + let num_of_gates = l[0].len(); + let next_power_of_two = num_of_gates.next_power_of_two(); + let pad_zeroes = next_power_of_two - num_of_gates; + + QuadraticArithmeticProgram { + num_of_public_inputs, + num_of_gates: next_power_of_two, + l: build_variable_polynomials(&apply_padding(l, pad_zeroes)), + r: build_variable_polynomials(&apply_padding(r, pad_zeroes)), + o: build_variable_polynomials(&apply_padding(o, pad_zeroes)), + } +} + +#[cfg(test)] +pub fn build_variable_polynomials(from_matrix: &[Vec]) -> Vec> { + from_matrix + .iter() + .map(|row| Polynomial::interpolate_fft::(row).unwrap()) + .collect() +} + +#[cfg(test)] +pub fn apply_padding(columns: &[Vec], pad_zeroes: usize) -> Vec> { + columns + .iter() + .map(|column| { + let mut new_column = column.clone(); + new_column.extend(vec![FrElement::zero(); pad_zeroes]); + new_column + }) + .collect() +}