Skip to content

Commit

Permalink
Implement R1CS and QAP (#10)
Browse files Browse the repository at this point in the history
* implement R1CS

* implement the draft of QAP

* implement QAP

* implement QAP
  • Loading branch information
Koukyosyumei authored Dec 21, 2024
1 parent 0586208 commit d29d3c5
Show file tree
Hide file tree
Showing 4 changed files with 440 additions and 0 deletions.
98 changes: 98 additions & 0 deletions book/src/zksnark/subsec2.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,50 @@ O &= \begin{bmatrix}
\end{bmatrix}
\end{align*}

**Implementation:**

```rust
fn dot<F: Field>(a: &Vec<F>, b: &Vec<F>) -> F {
let mut result = F::zero();
for (a_i, b_i) in a.iter().zip(b.iter()) {
result = result + a_i.clone() * b_i.clone();
}
result
}

#[derive(Debug, Clone)]
pub struct R1CS<F: Field> {
pub left: Vec<Vec<F>>,
pub right: Vec<Vec<F>>,
pub out: Vec<Vec<F>>,
pub m: usize,
pub d: usize,
}

impl<F: Field> R1CS<F> {
pub fn new(left: Vec<Vec<F>>, right: Vec<Vec<F>>, out: Vec<Vec<F>>) -> Self {
let d = left.len();
let m = if d == 0 { 0 } else { left[0].len() };
R1CS {
left,
right,
out,
m,
d,
}
}

pub fn is_satisfied(&self, a: &Vec<F>) -> bool {
let zero = F::zero();
self.left
.iter()
.zip(self.right.iter())
.zip(self.out.iter())
.all(|((l, r), o)| dot(&l, &a) * dot(&r, &a) - dot(&o, &a) == zero)
}
}
```

## Quadratic Arithmetic Program (QAP)

Recall that the prover aims to demonstrate knowledge of a witness \\(w\\) without revealing it. This is equivalent to knowing a vector \\(a\\) that satisfies \\((L \cdot a) \circ (R \cdot a) = O \cdot a\\), where \\(\circ\\) denotes the Hadamard (element-wise) product. However, evaluating this equivalence directly requires \\(\Omega(d)\\) operations, where \\(d\\) is the number of rows. To improve efficiency, we can convert this matrix comparison to a polynomial comparison, leveraging the Schwartz-Zippel Lemma, which allows us to check polynomial equality with \\(\Omega(1)\\) evaluations.
Expand Down Expand Up @@ -210,3 +254,57 @@ To address this discrepancy, we introduce a degree \\(d\\) polynomial \\(t(x) =
\end{equation}

where \\(h(x) = \frac{\ell(x) \cdot r(x) - o(x)}{t(x)}\\). This formulation allows us to maintain the desired polynomial relationships while accounting for the degree differences.

**Implementation:**

```rust
#[derive(Debug, Clone)]
pub struct QAP<'a, F: Field> {
pub r1cs: &'a R1CS<F>,
pub t: Polynomial<F>,
}

impl<'a, F: Field> QAP<'a, F> {
fn new(r1cs: &'a R1CS<F>) -> Self {
QAP {
r1cs: r1cs,
t: Polynomial::<F>::from_monomials(
&(1..=r1cs.d).map(|i| F::from_value(i)).collect::<Vec<F>>(),
),
}
}

fn generate_polynomials(&self, a: &Vec<F>) -> (Polynomial<F>, Polynomial<F>, Polynomial<F>) {
let left_dot_products = self
.r1cs
.left
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();
let right_dot_products = self
.r1cs
.right
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();
let out_dot_products = self
.r1cs
.out
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();

let x = (1..=self.r1cs.m)
.map(|i| F::from_value(i))
.collect::<Vec<F>>();
let left_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &left_dot_products);
let right_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &right_dot_products);
let out_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &out_dot_products);
(
left_interpolated_polynomial,
right_interpolated_polynomial,
out_interpolated_polynomial,
)
}
}
```
2 changes: 2 additions & 0 deletions myzkp/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub mod educational_protocols;
pub mod efield;
pub mod field;
pub mod polynomial;
pub mod qap;
pub mod r1cs;
pub mod ring;
pub mod utils;
// pub mod snark;
84 changes: 84 additions & 0 deletions myzkp/src/modules/qap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use num_traits::One;
use num_traits::Zero;

use crate::modules::field::Field;
use crate::modules::polynomial::Polynomial;
use crate::modules::r1cs::{dot, R1CS};

#[derive(Debug, Clone)]
pub struct QAP<'a, F: Field> {
pub r1cs: &'a R1CS<F>,
pub t: Polynomial<F>,
}

impl<'a, F: Field> QAP<'a, F> {
fn new(r1cs: &'a R1CS<F>) -> Self {
QAP {
r1cs: r1cs,
t: Polynomial::<F>::from_monomials(
&(1..=r1cs.d).map(|i| F::from_value(i)).collect::<Vec<F>>(),
),
}
}

fn generate_polynomials(&self, a: &Vec<F>) -> (Polynomial<F>, Polynomial<F>, Polynomial<F>) {
let left_dot_products = self
.r1cs
.left
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();
let right_dot_products = self
.r1cs
.right
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();
let out_dot_products = self
.r1cs
.out
.iter()
.map(|v| dot(&v, &a))
.collect::<Vec<F>>();

let x = (1..=self.r1cs.m)
.map(|i| F::from_value(i))
.collect::<Vec<F>>();
let left_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &left_dot_products);
let right_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &right_dot_products);
let out_interpolated_polynomial = Polynomial::<F>::interpolate(&x, &out_dot_products);
(
left_interpolated_polynomial,
right_interpolated_polynomial,
out_interpolated_polynomial,
)
}
}

#[cfg(test)]
mod tests {
use super::*;

use crate::modules::field::{FiniteFieldElement, ModEIP197};
use crate::modules::ring::Ring;

type F = FiniteFieldElement<ModEIP197>;

#[test]
fn test_qap_single_multiplication() {
// z = x * y
// (1, z, x, y) = (1, 3690, 82, 45)
let left = vec![vec![F::zero(), F::zero(), F::one(), F::zero()]];
let right = vec![vec![F::zero(), F::zero(), F::zero(), F::one()]];
let out = vec![vec![F::zero(), F::one(), F::zero(), F::zero()]];
let a = vec![
F::one(),
F::from_value(3690),
F::from_value(82),
F::from_value(45),
];
let r1cs = R1CS::new(left, right, out);
let qap = QAP::new(&r1cs);
let (_, _, _) = qap.generate_polynomials(&a);
}
}
Loading

0 comments on commit d29d3c5

Please sign in to comment.