-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Skeleton for Groth16 as part of Sparkling water bootcamp efforts (#612)
* 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
1 parent
2d2f4e3
commit 37f426c
Showing
12 changed files
with
653 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,3 +24,5 @@ ensure-no_std/target | |
# Files from fuzzers are inside a corpus folder | ||
**/corpus/** | ||
**/artifacts/** | ||
/.idea/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>(), | ||
], | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) | ||
} | ||
} |
Oops, something went wrong.