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()
+}