Skip to content

Commit

Permalink
Skeleton for Groth16 as part of Sparkling water bootcamp efforts (#612)
Browse files Browse the repository at this point in the history
* Skeleton for Groth16 as part of Sparkling water bootcamp efforts

* Solving linting

* Making clippy happy

* lr-o / t = h with no remainder

* having a problem with operate_with_self

* diego is a genius

* a

* alpha shift

* delta-shift

* introduce pairings

* minor renamings

* getting serious

* rearrangement

* toxic waste struct

* rearrange

* broke-down everything

* rearrange

* one step forward

* passes without shifts

* K(s) constructed!!!!!!!!

* pairings need to be multiplied

* a lot of tests. turn back here if you have trouble

* 10 times cleaner

* 100x cleaner

* 500x cleaner

* minor

* with and without zk

* functional zk-snark

* Added MSM to verify part

* Moved logic to setup | added simple tests

* major rearrangement

* another major refactor

* organize imports

* prover + verifier pippenger

* code organization

* generate_domain

* FFT integration

* rng -> chacha

* fold -> successors

* get rid of is_zk

* batch inverse

* more functional style code in groth16 setup

* powers of tau -> successors

* small tweak to qap & prover

* serde

* serde rearrangement

* clippy

* offset fft for h polynomial

* final

* clippy

* structurify groth16 prover

* padding for the prover

* missing newline

* Implemented review comments

* Fixing clippy

* minor renaming

* clippy

* padding corrected + one more test

* clippy

* perks

---------

Co-authored-by: Irfan Bozkurt <[email protected]>
Co-authored-by: irfan <[email protected]>
  • Loading branch information
3 people authored Nov 7, 2023
1 parent 2d2f4e3 commit 37f426c
Show file tree
Hide file tree
Showing 12 changed files with 653 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ ensure-no_std/target
# Files from fuzzers are inside a corpus folder
**/corpus/**
**/artifacts/**
/.idea/

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "examples/merkle-tree-cli"]
members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "examples/merkle-tree-cli"]
exclude = ["ensure-no_std"]
resolver = "2"

Expand Down
16 changes: 16 additions & 0 deletions provers/groth16/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "lambdaworks-groth16"
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.workspace = true
lambdaworks-crypto.workspace = true
rand_chacha = "0.3.1"
serde = "1.0"
serde_json = "1.0"
rand = "0.8.5"
3 changes: 3 additions & 0 deletions provers/groth16/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Lambdaworks Groth16 Prover

An incomplete and unoptimized implementation of the [Groth16](https://eprint.iacr.org/2016/260) protocol.
40 changes: 40 additions & 0 deletions provers/groth16/src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use lambdaworks_math::{
elliptic_curve::{
short_weierstrass::curves::bls12_381::{
curve::BLS12381Curve,
default_types::{FrElement as FE, FrField as FrF},
pairing::BLS12381AtePairing,
twist::BLS12381TwistCurve,
},
traits::{IsEllipticCurve, IsPairing},
},
field::element::FieldElement,
unsigned_integer::element::U256,
};
use rand::{Rng, SeedableRng};

pub type Curve = BLS12381Curve;
pub type TwistedCurve = BLS12381TwistCurve;

pub type FrElement = FE;
pub type FrField = FrF;

pub type Pairing = BLS12381AtePairing;

pub type G1Point = <BLS12381Curve as IsEllipticCurve>::PointRepresentation;
pub type G2Point = <BLS12381TwistCurve as IsEllipticCurve>::PointRepresentation;
pub type PairingOutput = FieldElement<<Pairing as IsPairing>::OutputField>;

pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7");

pub fn sample_fr_elem() -> FrElement {
let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001);
FrElement::new(U256 {
limbs: [
rng.gen::<u64>(),
rng.gen::<u64>(),
rng.gen::<u64>(),
rng.gen::<u64>(),
],
})
}
14 changes: 14 additions & 0 deletions provers/groth16/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub mod common;
pub mod qap;
pub mod test_circuits;

mod prover;
mod setup;
mod verifier;

pub use prover::{Proof, Prover};
pub use qap::QuadraticArithmeticProgram;
pub use setup::{setup, ProvingKey, VerifyingKey};
pub use verifier::verify;

pub use test_circuits::*;
149 changes: 149 additions & 0 deletions provers/groth16/src/prover.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use crate::{common::*, ProvingKey, QuadraticArithmeticProgram};
use lambdaworks_math::errors::DeserializationError;
use lambdaworks_math::traits::{Deserializable, Serializable};
use lambdaworks_math::{cyclic_group::IsGroup, msm::pippenger::msm};
use std::mem::size_of;

pub struct Proof {
pub pi1: G1Point,
pub pi2: G2Point,
pub pi3: G1Point,
}

impl Proof {
pub fn serialize(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
[
Self::serialize_commitment(&self.pi1),
Self::serialize_commitment(&self.pi2),
Self::serialize_commitment(&self.pi3),
]
.iter()
.for_each(|serialized| {
bytes.extend_from_slice(&(serialized.len() as u32).to_be_bytes());
bytes.extend_from_slice(serialized);
});
bytes
}

pub fn deserialize(bytes: &[u8]) -> Result<Self, DeserializationError>
where
Self: Sized,
{
let (offset, pi1) = Self::deserialize_commitment::<G1Point>(bytes, 0)?;
let (offset, pi2) = Self::deserialize_commitment::<G2Point>(bytes, offset)?;
let (_, pi3) = Self::deserialize_commitment::<G1Point>(bytes, offset)?;
Ok(Self { pi1, pi2, pi3 })
}

fn serialize_commitment<Commitment: Serializable>(cm: &Commitment) -> Vec<u8> {
cm.serialize()
}

// Repetitive. Same as in plonk/src/prover.rs
fn deserialize_commitment<Commitment: Deserializable>(
bytes: &[u8],
offset: usize,
) -> Result<(usize, Commitment), DeserializationError> {
let mut offset = offset;
let element_size_bytes: [u8; size_of::<u32>()] = bytes
.get(offset..offset + size_of::<u32>())
.ok_or(DeserializationError::InvalidAmountOfBytes)?
.try_into()
.map_err(|_| DeserializationError::InvalidAmountOfBytes)?;
let element_size = u32::from_be_bytes(element_size_bytes) as usize;
offset += size_of::<u32>();
let commitment = Commitment::deserialize(
bytes
.get(offset..offset + element_size)
.ok_or(DeserializationError::InvalidAmountOfBytes)?,
)?;
offset += element_size;
Ok((offset, commitment))
}
}

pub struct Prover;
impl Prover {
pub fn prove(w: &[FrElement], qap: &QuadraticArithmeticProgram, pk: &ProvingKey) -> Proof {
let h_coefficients = qap
.calculate_h_coefficients(w)
.iter()
.map(|elem| elem.representative())
.collect::<Vec<_>>();

let w = w
.iter()
.map(|elem| elem.representative())
.collect::<Vec<_>>();

// Sample randomness for hiding
let r = sample_fr_elem();
let s = sample_fr_elem();

// [π_1]_1
let pi1 = msm(&w, &pk.l_tau_g1)
.unwrap()
.operate_with(&pk.alpha_g1)
.operate_with(&pk.delta_g1.operate_with_self(r.representative()));

// [π_2]_2
let pi2 = msm(&w, &pk.r_tau_g2)
.unwrap()
.operate_with(&pk.beta_g2)
.operate_with(&pk.delta_g2.operate_with_self(s.representative()));

// [ƍ^{-1} * t(τ)*h(τ)]_1
let t_tau_h_tau_assigned_g1 = msm(
&h_coefficients,
&pk.z_powers_of_tau_g1[..h_coefficients.len()],
)
.unwrap();

// [ƍ^{-1} * (β*l(τ) + α*r(τ) + o(τ))]_1
let k_tau_assigned_prover_g1 = msm(
&w[qap.num_of_public_inputs..],
&pk.prover_k_tau_g1[..qap.num_of_private_inputs()],
)
.unwrap();

// [π_2]_1
let pi2_g1 = msm(&w, &pk.r_tau_g1)
.unwrap()
.operate_with(&pk.beta_g1)
.operate_with(&pk.delta_g1.operate_with_self(s.representative()));

// [π_3]_1
let pi3 = k_tau_assigned_prover_g1
.operate_with(&t_tau_h_tau_assigned_g1)
// s[π_1]_1
.operate_with(&pi1.operate_with_self(s.representative()))
// r[π_2]_1
.operate_with(&pi2_g1.operate_with_self(r.representative()))
// -rs[ƍ]_1
.operate_with(&pk.delta_g1.operate_with_self((-(&r * &s)).representative()));

Proof { pi1, pi2, pi3 }
}
}

#[cfg(test)]
mod tests {
use lambdaworks_math::elliptic_curve::traits::IsEllipticCurve;

use super::*;

#[test]
fn serde() {
let proof = Proof {
pi1: Curve::generator().operate_with_self(sample_fr_elem().representative()),
pi2: TwistedCurve::generator().operate_with_self(sample_fr_elem().representative()),
pi3: Curve::generator().operate_with_self(sample_fr_elem().representative()),
};
let deserialized_proof = Proof::deserialize(&proof.serialize()).unwrap();

assert_eq!(proof.pi1, deserialized_proof.pi1);
assert_eq!(proof.pi2, deserialized_proof.pi2);
assert_eq!(proof.pi3, deserialized_proof.pi3);
}
}
118 changes: 118 additions & 0 deletions provers/groth16/src/qap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use lambdaworks_math::{fft::polynomial::FFTPoly, polynomial::Polynomial};

use crate::common::*;

#[derive(Debug)]
pub struct QuadraticArithmeticProgram {
pub num_of_public_inputs: usize,
pub l: Vec<Polynomial<FrElement>>,
pub r: Vec<Polynomial<FrElement>>,
pub o: Vec<Polynomial<FrElement>>,
}

impl QuadraticArithmeticProgram {
pub fn from_variable_matrices(
num_of_public_inputs: usize,
l: &[Vec<FrElement>],
r: &[Vec<FrElement>],
o: &[Vec<FrElement>],
) -> 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<FrElement> {
let offset = &ORDER_R_MINUS_1_ROOT_UNITY;
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::new_monomial(FrElement::one(), self.num_of_gates())
- FrElement::one())
.evaluate_offset_fft(1, Some(degree), offset)
.unwrap();
FrElement::inplace_batch_inverse(&mut t).unwrap();

let h_evaluated = l
.iter()
.zip(&r)
.zip(&o)
.zip(&t)
.map(|(((l, r), o), t)| (l * r - o) * t)
.collect::<Vec<_>>();

Polynomial::interpolate_offset_fft(&h_evaluated, offset)
.unwrap()
.coefficients()
.to_vec()
}

fn apply_padding(columns: &[Vec<FrElement>], pad_zeroes: usize) -> Vec<Vec<FrElement>> {
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::<Vec<_>>()
}

fn build_variable_polynomials(from_matrix: &[Vec<FrElement>]) -> Vec<Polynomial<FrElement>> {
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
fn scale_and_accumulate_variable_polynomials(
&self,
w: &[FrElement],
degree: usize,
offset: &FrElement,
) -> [Vec<FrElement>; 3] {
[&self.l, &self.r, &self.o].map(|var_polynomials| {
var_polynomials
.iter()
.zip(w)
.map(|(poly, coeff)| poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0)))
.reduce(|poly1, poly2| poly1 + poly2)
.unwrap()
.evaluate_offset_fft(1, Some(degree), offset)
.unwrap()
})
}
}
Loading

0 comments on commit 37f426c

Please sign in to comment.