Skip to content

Commit

Permalink
Pinocchio (#16)
Browse files Browse the repository at this point in the history
* update

* update

* Update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* Update

* update

* update

* update

* Update

* update

* update
  • Loading branch information
Koukyosyumei authored Dec 28, 2024
1 parent f1175cf commit f2c56b3
Show file tree
Hide file tree
Showing 36 changed files with 1,464 additions and 655 deletions.
65 changes: 51 additions & 14 deletions book/src/zksnark/subsec3.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ pub fn malicious_schwartz_zippel_protocol<F: Field>(

## Third Protocol: Discrete Logarithm Assumption

To address this vulnerability, the Verifier must hide the randomly chosen input \\(s\\) from the Prover. This can be achieved using the discrete logarithm assumption: it is computationally hard to determine \\(s\\) from \\(\gamma\\), where \\(\gamma = g^s \bmod p\\). Thus, it's safe for the Verifier to send \\(\gamma\\), as the Prover cannot easily derive \\(s\\) from it.
To address this vulnerability, the Verifier must hide the randomly chosen input \\(s\\) from the Prover. This can be achieved using the discrete logarithm assumption: it is computationally hard to determine \\(s\\) from \\(\gamma\\), where \\(\gamma = g^s \bmod q\\). Thus, it's safe for the Verifier to send \\(\gamma\\), as the Prover cannot easily derive \\(s\\) from it.

An interesting property of polynomial exponentiation is:

Expand All @@ -219,6 +219,22 @@ Similarly, the Prover can evaluate \\(g^h = g^{H(s)}\\). The Verifier can then c

This approach prevents the Prover from obtaining \\(s\\) or \\(t = T(s)\\), making it impossible to send fake \\((h', p')\\) such that \\(p' = h't\\).

**Implementation:**

Suppose we are working in a finite field \(\mathbb{F}_q\) derived from a prime number \(q\). It’s important to note that

\\[g^{c_0} \cdot (g^{s^{1} \bmod q})^{c_1} \cdots (g^{s^{n} \bmod q})^{c_n} \bmod q \\]

is **not** equal to

\\[g^{c_0 + c_1 s^{1} + c_2 s^{2} + \cdots c_n s^{n}} \bmod q\\]

However, directly calculating \\(s^i\\) without taking modulo results in too large values to handle. To address this, we leverage Fermat's Little Theorem, which states that for a prime \\(q\\):

\\[a^{b \bmod q - 1} \bmod q\\]

Following this principle, our implementation computes \\(s^i\\) modulo \\(q - 1\\) to keep the values manageable.

```rust
pub struct Prover3<F: Field> {
pub p: Polynomial<F>,
Expand Down Expand Up @@ -534,16 +550,20 @@ pub fn zk_protocol<F: Field>(prover: &Prover5<F>, verifier: &Verifier5<F>) -> bo

The previously described protocol requires each verifier to generate unique random values, which becomes inefficient when a prover needs to demonstrate knowledge to multiple verifiers. To address this, we aim to eliminate the interaction between the prover and verifier. One effective solution is the use of a trusted setup.

Specifically, we let a thrid trusted party generate the secret seeds, which neither the prover nor the verifier can access. However, this prevents the verifier \\(\mathcal{B}\\) from calculating \\(u'^{r}\\) to check \\(u'^{r} = w'\\). Instead, the verifier can use a paring with bilinear mapping; \\(u'^{r} = w'\\) is equivalent to \\(e(u' = (g^{p})^{\delta}, g^{r}) = e(w'=(g^{p'})^{\delta}, g)\\).

In this tutorial, we adopt the optimal ate pairing, which is an asynmetric pairing and uses two different cyclic groups derived from elliptic curves; \\(e(g_1^a, g_2^b) = e(g_1^{ab}, g_2) = e(b_1, g_2^{ab})\\), where \\(g_1\\) and \\(g_2\\) are the generators of two different cyclic groups.

**Protocol (Trusted Setup):**

- ***Secret Seed:** A trusted third party generates the random values \\(s\\) and \\(r\\)*
- ***Proof Key:** Provided to the prover*
- *\\(\\{\gamma_1, \gamma_2, ..., \gamma_{n}\\}\\), where \\(\gamma_{i} = g^{(s^i)}\\)*
- *\\(\\{\gamma'\_1, \gamma'\_2, ..., \gamma'\_{n}\\}\\), where \\(\gamma_i' = g^{(s^{i})r}\\)*
- *\\(\\{\gamma_1, \gamma_2, ..., \gamma_{n}\\}\\), where \\(\gamma_{i} = g_1^{(s^i)}\\)*
- *\\(\\{\gamma'\_1, \gamma'\_2, ..., \gamma'\_{n}\\}\\), where \\(\gamma_i' = g_1^{(s^{i})r}\\)*
- ***Verification Key:** Distributed to verifiers*
- *\\(g^{r}\\)*
- *\\(g^{T(s)}\\)*
- *After distribution, the original \\(s\\) and \\(r\\) values are securely destroyed.*
- *\\(g_2^r\\)*
- *\\(g_2^t := g_2^{T(s)}\\)*
- *After distribution, the original secret seeds are securely destroyed.*


Then, the non-interactive protocol consists of two main parts: proof generation and verification.
Expand All @@ -552,20 +572,37 @@ Then, the non-interactive protocol consists of two main parts: proof generation

- *\\(\mathcal{A}\\) receives the proof key*
- *\\(\mathcal{A}\\) randomly selects \\(\delta\\) from field \\(\mathbb{F}\\).*
- *\\(\mathcal{A}\\) broadcast the proof \\(\pi = (u' = (g^{p})^{\delta}, v' = (g^{h})^{\delta}, w' = (g^{p'})^{\delta})\\)*


Since \\(r\\) is not shared and already destroyed, the verifier \\(\mathcal{B}\\) cannot calculate \\(u'^{r}\\) to check \\(u'^{r} = w'\\). Instead, the verifier can use a paring with bilinear mapping; \\(u'^{r} = w'\\) is equivalent to \\(e(u' = (g^{p})^{\delta}, g^{r}) = e(w'=(g^{p'})^{\delta}, g)\\).
- *\\(\mathcal{A}\\) broadcast the proof \\(\pi = (u' = (g_1^{p})^{\delta}, v' = (g_1^{h})^{\delta}, w' = (g_1^{p'})^{\delta})\\)*

**Protocol (Verification):**

- *\\(\mathcal{B}\\) receives the verification key.*
- *\\(\mathcal{B}\\) receives the proof \\(\pi\\).*
- *\\(\mathcal{B}\\) checks whether \\(e(u', g^{r}) = e(w', g)\\).*
- *\\(\mathcal{B}\\) checks whether \\(e(u', g) = e (v', g^{T(s)})\\).*
- *\\(\mathcal{B}\\) checks whether \\(e(u', g_2^r) = e(w', g_2)\\).*
- *\\(\mathcal{B}\\) checks whether \\(e(u', g_2) = e (v', g_2^t)\\).*

**Implementation:**

This tutorial utilize elliptic curves called `BN128` for pairing:

- First Group:
- Curve: \\(Y^2 = x^3 + 3\\) over the finite field \\(\mathbb{F}_{q}\\), where \\(q\\) is defined as 21888242871839275222246405745257275088696311157297823662689037894645226208583.
- Generator \\(\mathscr{g}_1 := (1, 2)\\)


- Second Group:
- Curve: \\(Y^2 = x^3 + 3\\) over the finite field \\(\mathbb{F} _{q^2}\\)
- Modulus polynomial for \\(\mathbb{F} _{q^2}\\): \\(x^2 + 1\\)
- Generator \\(\mathscr{g}_2\\) := (11559732032986387107991004021392285783925812861821192530917403151452391805634x + 10857046999023057135944570762232829481370756359578518086990519993285655852781, 4082367875863433681332203403145435568316851327593401208105741076214120093531x + 8495653923123431417604973247489272438418190587263600148770280649306958101930)

The orders of two groups are \\(\omega := \\) 21888242871839275222246405745257275088548364400416034343698204186575808495617. Thus, we evaluate the polynomials over the finite field \\(\mathbb{F}_{\omega}\\).

In the context of elliptic curves, the power of a generator corresponds to the multiplication of an elliptic curve point. This property is reflected in the ate pairing on BN128, which satisfies:

\\[
e(a \mathscr{g}_1, b \mathscr{g}_2) = e(ab \mathscr{g}_1, \mathscr{g}_2) = e(\mathscr{g}_1, ab\mathscr{g}_2)
\\]

```rust
pub struct ProofKey {
alpha: Vec<G1Point>,
Expand Down Expand Up @@ -627,12 +664,12 @@ pub fn prove(p: &Polynomial<FqOrder>, t: &Polynomial<FqOrder>, proof_key: &Proof
}

pub fn verify(g2: &G2Point, proof: &Proof, vk: &VerificationKey) -> bool {
// Check e(u', g^r) = e(w', g)
// Check e(u', rG_2) = e(w', G_2)
let pairing1 = optimal_ate_pairing(&proof.u_prime, &vk.g_r);
let pairing2 = optimal_ate_pairing(&proof.w_prime, g2);
let check1 = pairing1 == pairing2;

// Check e(u', g^t) = e(v', g)
// Check e(u', G_2) = e(v', tG_2)
let pairing3 = optimal_ate_pairing(&proof.u_prime, g2);
let pairing4 = optimal_ate_pairing(&proof.v_prime, &vk.g_t_s);
let check2 = pairing3 == pairing4;
Expand Down
Loading

0 comments on commit f2c56b3

Please sign in to comment.