Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOT 164: Add Parser #15

Merged
merged 23 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kzg/src/srs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::types::{G1Point, G2Point};
///
/// The `Srs` struct represents the structured reference string used in the KZG scheme,
/// containing precomputed values necessary for commitment and verification.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Srs {
/// Points in G1, each equals to generator point multiplied by the secret's powers.
g1_points: Vec<G1Point>,
Expand Down
4 changes: 2 additions & 2 deletions plonk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ ark-poly = "0.4.2"
ark-ec = "0.4.2"
ark-bls12-381 = "0.4.0"
ark-serialize = "0.4.2"
ark-std = "0.4.0"
rand = "0.8.5"
ark-std = { version = "0.4.0", default-features = false }
sha2 = "0.10"
kzg = { path = "../kzg" }
clap = { version = "4.5.4", features = ["derive"] }

[dependencies.digest]
version = "0.10"
Expand Down
96 changes: 49 additions & 47 deletions plonk/examples/example.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ark_bls12_381::Fr;
use sha2::Sha256;

use kzg::srs::Srs;
use plonk::circuit::Circuit;
use plonk::prover;
use plonk::verifier;
Expand All @@ -9,55 +10,56 @@ fn main() {
// Function: xy + 3x^2 + xyz = 11

// init a new circuit
let compile_circuit = Circuit::default()
.add_multiplication_gate(
(0, 1, Fr::from(1)),
(1, 0, Fr::from(2)),
(0, 3, Fr::from(2)),
Fr::from(0),
)
.add_multiplication_gate(
(1, 1, Fr::from(1)),
(0, 0, Fr::from(1)),
(0, 2, Fr::from(1)),
Fr::from(0),
)
.add_multiplication_gate(
(2, 1, Fr::from(1)),
(2, 6, Fr::from(3)),
(1, 3, Fr::from(3)),
Fr::from(0),
)
.add_addition_gate(
(0, 4, Fr::from(2)),
(2, 2, Fr::from(3)),
(0, 5, Fr::from(5)),
Fr::from(0),
)
.add_multiplication_gate(
(2, 0, Fr::from(2)),
(1, 4, Fr::from(3)),
(1, 5, Fr::from(6)),
Fr::from(0),
)
.add_addition_gate(
(2, 3, Fr::from(5)),
(2, 4, Fr::from(6)),
(2, 5, Fr::from(11)),
Fr::from(0),
)
.add_constant_gate(
(0, 6, Fr::from(3)),
(1, 6, Fr::from(0)),
(1, 2, Fr::from(3)),
Fr::from(0),
)
.compile()
.unwrap();
let mut circuit = Circuit::default();
circuit.add_multiplication_gate(
(0, 1, Fr::from(1)),
(1, 0, Fr::from(2)),
(0, 3, Fr::from(2)),
Fr::from(0),
);
circuit.add_multiplication_gate(
(1, 1, Fr::from(1)),
(0, 0, Fr::from(1)),
(0, 2, Fr::from(1)),
Fr::from(0),
);
circuit.add_multiplication_gate(
(2, 1, Fr::from(1)),
(2, 6, Fr::from(3)),
(1, 3, Fr::from(3)),
Fr::from(0),
);
circuit.add_addition_gate(
(0, 4, Fr::from(2)),
(2, 2, Fr::from(3)),
(0, 5, Fr::from(5)),
Fr::from(0),
);
circuit.add_multiplication_gate(
(2, 0, Fr::from(2)),
(1, 4, Fr::from(3)),
(1, 5, Fr::from(6)),
Fr::from(0),
);
circuit.add_addition_gate(
(2, 3, Fr::from(5)),
(2, 4, Fr::from(6)),
(2, 5, Fr::from(11)),
Fr::from(0),
);
circuit.add_constant_gate(
(0, 6, Fr::from(3)),
(1, 6, Fr::from(0)),
(1, 2, Fr::from(3)),
Fr::from(0),
);

let compiled_circuit = circuit.compile().unwrap();

// generate proof
let proof = prover::generate_proof::<Sha256>(&compile_circuit);
let srs = Srs::new(compiled_circuit.size);
let proof = prover::generate_proof::<Sha256>(&compiled_circuit, srs.clone());

// verify proof
assert!(verifier::verify::<Sha256>(&compile_circuit, proof).is_ok());
assert!(verifier::verify::<Sha256>(&compiled_circuit, srs, proof).is_ok());
}
41 changes: 25 additions & 16 deletions plonk/plonk.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# PLONK implementation

## Overview

This is an implementation of PLONK. You all can use this one as a library.

## Details
This implementation closely follows the specifications outlined in the
[PLONK paper](https://eprint.iacr.org/2019/953.pdf). We have implemented all the relevant rounds
for both proof generation and verification. For detailed code, please

This implementation closely follows the specifications outlined in the
[PLONK paper](https://eprint.iacr.org/2019/953.pdf). We have implemented all the relevant rounds
for both proof generation and verification. For detailed code, please
refer to the `prover.rs`and `verifier.rs` files located in the `src`
directory.
directory.

### What we use ?
We utilize the Bls12-381 curve for KZG commitment in this implementation. To learn more about this

We utilize the Bls12-381 curve for KZG commitment in this implementation. To learn more about this
curve, please refer to [this resource](https://github.com/sota-zk-lab/zkp-documents/blob/main/terms/bls12-381.md),
and for further insights into KZG commitment, you can explore
and for further insights into KZG commitment, you can explore
[our documentation](https://github.com/sota-zk-lab/zkp-documents/blob/main/terms/polynomial-commitment/100_kate_commitment.md).

All the dependencies utilized in this project are sourced from the `ark` (or `arkworks`) crates. For more information, please visit [arkworks.rs](https://arkworks.rs/).
All the dependencies utilized in this project are sourced from the `ark` (or `arkworks`) crates. For more information,
please visit [arkworks.rs](https://arkworks.rs/).

### Set up

Expand All @@ -30,19 +34,21 @@ Before proceeding, it's advisable to review the documentation on how to utilize
```
cargo run --example plonk-example
```
The above code will run the `main` function in `example.rs` files located in the `examples`

The above code will run the `main` function in `example.rs` files located in the `examples`
directory, which is the example usage of this library.

### Gates

> [!NOTE]
> For now, we have not implemented a transformation function to convert plain equations
> into circuits automatically. So, we're adding gates manually. We will endeavor to create
> this function as soon as possible. If you're interested in contributing to this project
> For now, we have not implemented a transformation function to convert plain equations
> into circuits automatically. So, we're adding gates manually. We will endeavor to create
> this function as soon as possible. If you're interested in contributing to this project
> in any capacity, feel free to raise the issue or create a pull request.

Let's take equation: `x^2 + y^2 = z^2` with `x = 3, y = 4, z = 5` as an example.
Prior to circuit generation, it's essential to parse it into smaller equations:

```
- x * x = m
- y * y = n
Expand All @@ -55,38 +61,41 @@ Consider each equation above as a gate. Here is the table of gates:
![gate_explanation_01.PNG](attachments/gate_explanation_01.png)

In the first equations, both the left and right parts of the left side are `x`, which means
they are the same. So, in the first gate, the values of `a` and `b` must be equal. It holds
they are the same. So, in the first gate, the values of `a` and `b` must be equal. It holds
true for the other gates as well. We denote this with colorful lines in the picture.

We store the value of each wire in gates in a 2-D vector, where the first dimension is either `0`, `1` or `2`,
corresponding to `a`, `b` or `c` respectively. Then, instead of storing each wire with its actual position
corresponding to `a`, `b` or `c` respectively. Then, instead of storing each wire with its actual position
(e.g., wire `a` in gate `0` is `(0, 0)`, wire `c` in gate `3` is `(2, 3)`), we store it with its copied position.

In the example above, wire `a` in gate `0` is a copy of wire `b` in gate `0`, so we store `(1, 0)` for wire `a`
and `(0, 0)` for wire `b`. Similarly, wire `c` in gate `0` is a copy of wire `a` in gate `3`, so we store `(0, 3)`
In the example above, wire `a` in gate `0` is a copy of wire `b` in gate `0`, so we store `(1, 0)` for wire `a`
and `(0, 0)` for wire `b`. Similarly, wire `c` in gate `0` is a copy of wire `a` in gate `3`, so we store `(0, 3)`
for wire `c` and `(2,0)` for wire `a`. The following picture shows the full positions:

![gate_explanation_02.PNG](attachments/gate_explanation_02.png)

Now, you can create any circuit on your own and run PLONK on it.

> [!TIP]
> If you are not familiar with the workings of PLONK, I recommend reading our slide
> If you are not familiar with the workings of PLONK, I recommend reading our slide
> [here](https://github.com/sota-zk-lab/zkp-documents/blob/main/presentations/plonk_implementation.pptx)

### Run

This library comes with some unit and integration tests. Run these tests with this command:

```
cargo test
```

You can view each round in generating proof step and verifying step does by:

```
cargo test -- --nocapture
```

## References

[Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge](https://eprint.iacr.org/2019/953.pdf)<br/>
Ariel Gabizon, Zachary J. Williamson, Oana Ciobotaru

Expand Down
55 changes: 29 additions & 26 deletions plonk/src/challenge.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::marker::PhantomData;

use ark_bls12_381::Fr;
use ark_ff::UniformRand;
use ark_ff::{UniformRand, Zero};
use ark_serialize::{CanonicalSerialize, Write};
use rand::rngs::StdRng;
use rand::SeedableRng;
use ark_std::rand::rngs::StdRng;
use ark_std::rand::SeedableRng;
use sha2::Digest;

use kzg::commitment::KzgCommitment;
Expand All @@ -17,27 +17,26 @@ pub struct ChallengeGenerator<T: Digest + Default> {
data: Option<Vec<u8>>,
generated: bool,

#[allow(dead_code)]
/// Phantom data for annotation purposes.
// Phantom data for annotation purposes.
_phantom_data: PhantomData<T>,
}

impl<T: Digest + Default> ChallengeGenerator<T> {
/// Creates a new `ChallengeGenerator` instance from a list of commitments.
/// Creates a new `ChallengeGenerator` from a slice of KZG commitments.
///
/// # Parameters
/// # Arguments
///
/// - `kzg_commitment`: A slice containing the commitments.
/// * `kzg_commitments` - A slice of KZG commitments used to initialize the generator.
///
/// # Returns
///
/// A new `ChallengeGenerator` instance.
pub fn from_commitment(kzg_commitment: &[KzgCommitment]) -> Self {
let mut challenge_parse = Self::default();
for commitment in kzg_commitment {
challenge_parse.feed(commitment);
/// A `ChallengeGenerator` initialized with the provided commitments.
pub fn from_commitments(kzg_commitments: &[KzgCommitment]) -> Self {
let mut challenge_generator = Self::default();
for commitment in kzg_commitments {
challenge_generator.feed(commitment);
}
challenge_parse
challenge_generator
}
}

Expand All @@ -63,9 +62,11 @@ impl<T: Digest + Default> ChallengeGenerator<T> {
panic!("I'm hungry! Feed me something first");
}
self.generated = true;
let mut seed: [u8; 8] = Default::default();
seed.copy_from_slice(&self.data.clone().unwrap_or_default()[0..8]);
let seed = u64::from_le_bytes(seed);
let seed = self
.data
.clone()
.map(|data| u64::from_le_bytes(data[..8].try_into().unwrap()))
.expect("No data to generate seed from");
StdRng::seed_from_u64(seed)
}

Expand All @@ -80,8 +81,11 @@ impl<T: Digest + Default> ChallengeGenerator<T> {
/// An array of generated challenges.
pub fn generate_challenges<const N: usize>(&mut self) -> [Fr; N] {
let mut rng = self.generate_rng_with_seed();
let points = [0; N];
points.map(|_| Fr::rand(&mut rng))
let mut points = [Fr::zero(); N];
for point in &mut points {
*point = Fr::rand(&mut rng);
}
points
}
}

Expand All @@ -92,7 +96,7 @@ struct HashMarshaller<'a, H: Digest>(&'a mut H);
impl<'a, H: Digest> Write for HashMarshaller<'a, H> {
#[inline]
fn write(&mut self, buf: &[u8]) -> ark_std::io::Result<usize> {
Digest::update(self.0, buf);
self.0.update(buf);
Ok(buf.len())
}

Expand All @@ -104,10 +108,9 @@ impl<'a, H: Digest> Write for HashMarshaller<'a, H> {

#[cfg(test)]
mod tests {
use std::ops::Mul;

use ark_ec::{AffineRepr, CurveGroup};
use sha2::Sha256;
use std::ops::Mul;

use crate::types::G1Point;

Expand All @@ -119,16 +122,16 @@ mod tests {
let commitment2 = KzgCommitment(G1Point::generator().mul(Fr::from(2)).into_affine());
let commitments1: [KzgCommitment; 2] = [commitment1.clone(), commitment2.clone()];
let [a, aa, aaa] =
ChallengeGenerator::<Sha256>::from_commitment(&commitments1).generate_challenges();
ChallengeGenerator::<Sha256>::from_commitments(&commitments1).generate_challenges();

let commitments2: [KzgCommitment; 1] = [commitment2.clone()];
let [b] =
ChallengeGenerator::<Sha256>::from_commitment(&commitments2).generate_challenges();
ChallengeGenerator::<Sha256>::from_commitments(&commitments2).generate_challenges();
assert_ne!(a, b, "should be different");

let commitments3: [KzgCommitment; 2] = [commitment1.clone(), commitment2.clone()];
let [c, cc, ccc] =
ChallengeGenerator::<Sha256>::from_commitment(&commitments3).generate_challenges();
ChallengeGenerator::<Sha256>::from_commitments(&commitments3).generate_challenges();
assert_eq!(a, c, "should be equal");
assert_eq!(aa, cc, "should be equal");
assert_eq!(aaa, ccc, "should be equal");
Expand All @@ -139,7 +142,7 @@ mod tests {
fn safe_guard() {
let commitment1 = KzgCommitment(G1Point::generator().mul(Fr::from(1)).into_affine());
let commitments1: [KzgCommitment; 1] = [commitment1.clone()];
let mut generator = ChallengeGenerator::<Sha256>::from_commitment(&commitments1);
let mut generator = ChallengeGenerator::<Sha256>::from_commitments(&commitments1);
let [_a, _aa, _aaa] = generator.generate_challenges();
let [_a, _aa, _aaa] = generator.generate_challenges();
}
Expand Down
Loading
Loading