Skip to content

Commit

Permalink
Implement low-degree-test (#12)
Browse files Browse the repository at this point in the history
* Impl ldt test with MerkleTree.
* Refact kzg.
* Add commit-and-open by index for MerkleTree in ldt.
* bugfix for MerkleTree.
  • Loading branch information
SuccinctPaul authored Aug 26, 2023
1 parent 4b812f3 commit b1968fd
Show file tree
Hide file tree
Showing 29 changed files with 1,742 additions and 501 deletions.
3 changes: 1 addition & 2 deletions 15_kzg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ bls12_381 = "0.8.0"
rand = "0.8.5"
rand_core = { version = "0.6.4", default-features = false, features = ["std"] }
rayon = "1.7.0"
log = "0.4.19"
num-bigint = "0.4.3"
sha3 = "0.10.6"
47 changes: 47 additions & 0 deletions 15_kzg/src/kzg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
mod param;
mod prover;
mod verifier;

use bls12_381::Scalar;
use ff::Field;
use pairing::Engine;

pub struct KZGProof<E: Engine> {
cm: E::G1, // commit of p(x)
eval: E::Fr, // eval for p(z)
pi: E::G1, // aka.π, commit of q(x), q = p(x)-p(z)/x-z
}

impl<E: Engine> KZGProof<E> {
fn new(cm: E::G1, eval: E::Fr, pi: E::G1) -> Self {
Self { cm, eval, pi }
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::kzg::param::ParamKzg;
use crate::kzg::prover::Prover;
use crate::kzg::verifier::Verifier;
use crate::poly::Polynomial;
use bls12_381::Bls12;
use ff::PrimeField;

#[test]
fn test_kzg_protocol() {
let k = 4;
let poly = Polynomial::random(3);

// setup
let param = ParamKzg::<Bls12>::setup(k);

// prove
let prover = Prover::init(param.clone());
let proof = prover.prover(&poly);

// verify
let verifier = Verifier::init(param);
verifier.verify(proof);
}
}
80 changes: 80 additions & 0 deletions 15_kzg/src/kzg/param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::msm::small_multiexp;
use crate::poly::Polynomial;
use ff::{Field, PrimeField};
use group::prime::PrimeCurveAffine;
use pairing::Engine;
use rand_core::OsRng;
use std::fmt::Debug;

// The SRS
#[derive(Clone)]
pub struct ParamKzg<E: Engine> {
pub(crate) k: usize,
pub(crate) n: usize,
pub pow_tau_g1: Vec<E::G1>,
pub pow_tau_g2: Vec<E::G2>,
}

impl<E: Engine + Debug> ParamKzg<E>
where
E::Fr: PrimeField,
{
fn new(k: usize) -> Self {
Self::setup(k)
}

// Generate the SRS
pub fn setup(k: usize) -> Self {
let n = 1 << k;

let tau = E::Fr::random(OsRng);

// obtain: s, ..., s^i,..., s^n
let powers_of_tau: Vec<E::Fr> = (0..n)
.into_iter()
.scan(E::Fr::ONE, |acc, _| {
let v = *acc;
*acc *= tau;
Some(v)
})
.collect();

// obtain [s]1
let pow_tau_g1: Vec<E::G1> = powers_of_tau
.iter()
.map(|tau_pow| E::G1Affine::generator() * tau_pow)
.collect();

// obtain [s]2
let pow_tau_g2: Vec<E::G2> = powers_of_tau
.iter()
.map(|tau_pow| E::G2Affine::generator() * tau_pow)
.collect();

Self {
k,
n,
pow_tau_g1,
pow_tau_g2,
}
}

// unify ti with commit_lagrange
pub fn eval_at_tau_g1(&self, poly: &Polynomial<E::Fr>) -> E::G1 {
let mut scalars = Vec::with_capacity(poly.len());
scalars.extend(poly.coeffs().iter());
let bases = &self.pow_tau_g1;
let size = scalars.len();
assert!(bases.len() >= size);
small_multiexp(&scalars, &bases[0..size])
}

pub fn eval_at_tau_g2(&self, poly: &Polynomial<E::Fr>) -> E::G2 {
let mut scalars = Vec::with_capacity(poly.len());
scalars.extend(poly.coeffs().iter());
let bases = &self.pow_tau_g2;
let size = scalars.len();
assert!(bases.len() >= size);
small_multiexp(&scalars, &bases[0..size])
}
}
100 changes: 100 additions & 0 deletions 15_kzg/src/kzg/prover.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::kzg::param::ParamKzg;
use crate::kzg::KZGProof;
use crate::poly::Polynomial;
use crate::transcript::default::Keccak256Transcript;
use crate::transcript::Transcript;
use ff::{BitViewSized, Field, PrimeField};
use pairing::Engine;
use std::ops::{MulAssign, SubAssign};

pub struct Prover<E: Engine> {
param: ParamKzg<E>,
}

impl<E: Engine> Prover<E> {
pub fn init(param: ParamKzg<E>) -> Self {
Self { param }
}

pub fn prover(&self, poly: &Polynomial<E::Fr>) -> KZGProof<E> {
// 1. commit
let cm = self.commit(poly);

// 2. challenge z.
let mut transcript_1 = Keccak256Transcript::<E::Fr>::default();
let z = transcript_1.challenge();
// 3. eval z.
let eval = poly.evaluate(z.clone());

// 4. open
let pi = self.open(poly, &z);

KZGProof::new(cm, eval, pi)
}

// return the commit of p
fn commit(&self, poly: &Polynomial<E::Fr>) -> E::G1 {
self.param.eval_at_tau_g1(poly)
}

// return the commit of q, aka.pi, the proof.
fn open(&self, poly: &Polynomial<E::Fr>, z: &E::Fr) -> E::G1 {
// q = ( p(x) - p(z) ) / x-z
let q_coeff = Self::kate_division(&poly.coeffs(), z.clone());
let q = Polynomial::from_coeffs(q_coeff);
// the proof is evaluating the Q at tau in G1
self.commit(&q)
}

// Divides polynomial `a` in `X` by `X - b` with no remainder.
// q(x) = f(x)-f(z)/x-z
fn kate_division(a: &Vec<E::Fr>, z: E::Fr) -> Vec<E::Fr> {
let b = -z;
let a = a.into_iter();

let mut q = vec![E::Fr::ZERO; a.len() - 1];

let mut tmp: E::Fr = E::Fr::ZERO;
for (q, r) in q.iter_mut().rev().zip(a.rev()) {
let mut lead_coeff = *r;
lead_coeff.sub_assign(&tmp);
*q = lead_coeff;
tmp = lead_coeff;
tmp.mul_assign(&b);
}
q
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::kzg::param::ParamKzg;
use crate::kzg::prover::Prover;
use crate::kzg::verifier::Verifier;
use crate::poly::Polynomial;
use bls12_381::{Bls12, Scalar};
use ff::{Field, PrimeField};

#[test]
fn test_div() {
// division: -2+x+x^2 = (x-1)(x+2)
let division = vec![Scalar::from_u128(2).neg(), Scalar::ONE, Scalar::ONE];

// dividor: 2+x
// dividor: -1+x
let coeffs = vec![Scalar::ONE.neg(), Scalar::ONE];
let dividor = Polynomial::from_coeffs(coeffs);

// target:
// quotient poly: 2+x
// remainder poly: 0
let target_qoutient = vec![Scalar::from_u128(2), Scalar::ONE];

// q(x) = f(x)-f(z)/x-z
let z = Scalar::ONE;
let actual_qoutient = Prover::<Bls12>::kate_division(&division, z);

assert_eq!(actual_qoutient, target_qoutient);
}
}
58 changes: 58 additions & 0 deletions 15_kzg/src/kzg/verifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Verifies that `points` exists in `proof`

use crate::kzg::param::ParamKzg;
use crate::kzg::KZGProof;
use crate::poly::Polynomial;
use crate::transcript::default::Keccak256Transcript;
use crate::transcript::Transcript;
use bls12_381::Scalar;
use ff::{Field, PrimeField};
use group::prime::PrimeCurveAffine;
use group::Curve;
use pairing::Engine;
use std::fmt::Debug;
use std::ops::Neg;

pub struct Verifier<E: Engine> {
param: ParamKzg<E>,
}

impl<E: Engine> Verifier<E> {
pub fn init(param: ParamKzg<E>) -> Self {
Self { param }
}

// verify proof by pairing:
// check e(π, [x−z]_2 ) = e(cm−[p(z)]_1, g2)
// => e(g1, g2)^{q(x)*(x-z)} = e(g1, g2)^{p(x)-p(z)}
// => q(x)*(x-z) = p(x)-p(z)
// -> same as Prove::open.
pub fn verify(&self, proof: KZGProof<E>) {
let vanish_poly = |z: E::Fr| {
let coeffs = vec![z.neg(), E::Fr::ONE];
Polynomial::from_coeffs(coeffs)
};

// 1. challenge z.
let mut transcript_1 = Keccak256Transcript::<E::Fr>::default();
let z = transcript_1.challenge();

// 2. prepare poly for pairing.
// compute: x-z
let vanish_poly = vanish_poly(z);
let eval_poly = Polynomial::from_coeffs(vec![proof.eval]);

// 3.pairing
// e(pi, [x-z]2)
let e1 = E::pairing(
&proof.pi.to_affine(),
&self.param.eval_at_tau_g2(&vanish_poly).to_affine(),
);
// e(cm-[p(z)]1, g2)
let e2 = E::pairing(
&(proof.cm - self.param.eval_at_tau_g1(&eval_poly)).to_affine(),
&E::G2Affine::generator(),
);
assert_eq!(e1, e2, "Verify: failed for pairing.");
}
}
Loading

0 comments on commit b1968fd

Please sign in to comment.