Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- master
env:
RUST_BACKTRACE: 1
RUSTFLAGS: "--emit asm -C llvm-args=-x86-asm-syntax=intel"

jobs:
style:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ark-std = { version = "^0.4.0", default-features = false }
ark-relations = { version = "^0.4.0", default-features = false, optional = true }
ark-r1cs-std = { version = "^0.4.0", default-features = false, optional = true }
hashbrown = { version = "0.9", optional = true }
colored = "2"

digest = "0.9"
rayon = { version = "1", optional = true }
Expand Down
22 changes: 21 additions & 1 deletion src/multilinear_pc/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub struct UniversalParams<E: Pairing> {
pub h: E::G2Affine,
/// g^randomness
pub g_mask: Vec<E::G1Affine>,
/// h^randomness
pub h_mask: Vec<E::G2Affine>,
}

/// Public Parameter used by prover
Expand Down Expand Up @@ -51,10 +53,12 @@ pub struct VerifierKey<E: Pairing> {
pub h: E::G2Affine,
/// g^t1, g^t2, ...
pub g_mask_random: Vec<E::G1Affine>,
/// h^t1, h^t2,...
pub h_mask_random: Vec<E::G2Affine>,
}

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
/// commitment
/// PST commitment on the G1 group
pub struct Commitment<E: Pairing> {
/// number of variables
pub nv: usize,
Expand All @@ -63,8 +67,24 @@ pub struct Commitment<E: Pairing> {
}

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
/// PST Commitment on the G2 group
pub struct CommitmentG2<E: Pairing> {
/// number of variables
pub nv: usize,
/// product of g as described by the vRAM paper
pub h_product: E::G2Affine,
}

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
/// proof of opening
pub struct Proof<E: Pairing> {
/// Evaluation of quotients
pub proofs: Vec<E::G2Affine>,
}

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
/// PST Proof of opening on G1 (so commitment is on G2)
pub struct ProofG1<E: Pairing> {
/// Evaluation of quotients
pub proofs: Vec<E::G1Affine>,
}
192 changes: 171 additions & 21 deletions src/multilinear_pc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use self::data_structures::{CommitmentG2, ProofG1};

use crate::multilinear_pc::data_structures::{
Commitment, CommitterKey, Proof, UniversalParams, VerifierKey,
};
Expand All @@ -14,7 +16,10 @@ use ark_std::ops::Mul;
use ark_std::rand::RngCore;
use ark_std::vec::Vec;
use ark_std::UniformRand;

use rayon::prelude::*;
use std::thread;
// use rayon::iter::ParallelIterator;
// use rayon::prelude::{IndexedParallelIterator, IntoParallelIterator};
/// data structures used by multilinear extension commitment scheme
pub mod data_structures;

Expand Down Expand Up @@ -46,8 +51,8 @@ impl<E: Pairing> MultilinearPC<E> {
if i != 0 {
let mul = eq.pop_back().unwrap().evaluations;
base = base
.into_iter()
.zip(mul.into_iter())
.into_par_iter()
.zip(mul.into_par_iter())
.map(|(a, b)| a * &b)
.collect();
}
Expand Down Expand Up @@ -94,15 +99,22 @@ impl<E: Pairing> MultilinearPC<E> {
let g_table = FixedBase::get_window_table(scalar_bits, window_size, g.into_group());
E::G1::normalize_batch(&FixedBase::msm(scalar_bits, window_size, &g_table, &t))
};

let h_mask = {
let window_size = FixedBase::get_mul_window_size(num_vars);
let h_table = FixedBase::get_window_table(scalar_bits, window_size, h.into_group());
E::G2::normalize_batch(&FixedBase::msm(scalar_bits, window_size, &h_table, &t))
};
// end_timer!(vp_generation_timer);

UniversalParams {
num_vars,
g,
g_mask,
h,
powers_of_g,
powers_of_h,
g_mask,
h_mask,
}
}

Expand All @@ -127,6 +139,7 @@ impl<E: Pairing> MultilinearPC<E> {
g: params.g,
h: params.h,
g_mask_random: (&params.g_mask[to_reduce..]).to_vec(),
h_mask_random: (&params.h_mask[to_reduce..]).to_vec(),
};
(ck, vk)
}
Expand All @@ -137,17 +150,29 @@ impl<E: Pairing> MultilinearPC<E> {
polynomial: &impl MultilinearExtension<E::ScalarField>,
) -> Commitment<E> {
let nv = polynomial.num_vars();
let scalars: Vec<_> = polynomial
.to_evaluations()
.into_iter()
.map(|x| x.into_bigint())
.collect();
let g_product =
<E::G1 as VariableBaseMSM>::msm_bigint(&ck.powers_of_g[0], scalars.as_slice())
.into_affine();
let scalars: Vec<_> = polynomial.to_evaluations();
debug_assert!(scalars.len() == ck.powers_of_g[0].len());
let g_product = <E::G1 as VariableBaseMSM>::msm(&ck.powers_of_g[0], &scalars[..])
.unwrap()
.into_affine();
Commitment { nv, g_product }
}

/// commit the given polynomial using the G2 group as a basis
/// That means the opening will be in G1.
pub fn commit_g2(
ck: &CommitterKey<E>,
polynomial: &impl MultilinearExtension<E::ScalarField>,
) -> CommitmentG2<E> {
let nv = polynomial.num_vars();
let scalars: Vec<_> = polynomial.to_evaluations();
debug_assert!(scalars.len() == ck.powers_of_h[0].len());
let h_product = <E::G2 as VariableBaseMSM>::msm(&ck.powers_of_h[0], &scalars[..])
.unwrap()
.into_affine();
CommitmentG2 { nv, h_product }
}

/// On input a polynomial `p` and a point `point`, outputs a proof for the same.
pub fn open(
ck: &CommitterKey<E>,
Expand All @@ -161,14 +186,16 @@ impl<E: Pairing> MultilinearPC<E> {

r[nv] = polynomial.to_evaluations();

let mut proofs = Vec::new();
let mut thread_handles = vec![];
for i in 0..nv {
let k = nv - i;
let point_at_k = point[i];
q[k] = (0..(1 << (k - 1)))
.into_par_iter()
.map(|_| E::ScalarField::zero())
.collect();
r[k - 1] = (0..(1 << (k - 1)))
.into_par_iter()
.map(|_| E::ScalarField::zero())
.collect();
for b in 0..(1 << (k - 1)) {
Expand All @@ -177,19 +204,113 @@ impl<E: Pairing> MultilinearPC<E> {
+ &(r[k][(b << 1) + 1] * &point_at_k);
}
let scalars: Vec<_> = (0..(1 << k))
.map(|x| q[k][x >> 1].into_bigint()) // fine
.into_par_iter()
.map(|x| q[k][x >> 1]) // fine
.collect();

let pi_h =
<E::G2 as VariableBaseMSM>::msm_bigint(&ck.powers_of_h[i], &scalars).into_affine(); // no need to move outside and partition
proofs.push(pi_h);
let ph = ck.powers_of_h[i].clone();
debug_assert!(ph.len() == scalars.len());
thread_handles.push(thread::spawn(move || {
<E::G2 as VariableBaseMSM>::msm(&ph, &scalars[..])
.unwrap()
.into_affine()
}));
}
print!("Waiting for threads to finish...");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice :)

let proofs = thread_handles
.into_iter()
.map(|h| h.join().unwrap())
.collect();

Proof { proofs: proofs }
}

/// Create PST opening proof in G1 (with a commitment on G2)
pub fn open_g1(
ck: &CommitterKey<E>,
polynomial: &impl MultilinearExtension<E::ScalarField>,
point: &[E::ScalarField],
) -> ProofG1<E> {
assert_eq!(polynomial.num_vars(), ck.nv, "Invalid size of polynomial");
let nv = polynomial.num_vars();
let mut r: Vec<Vec<E::ScalarField>> = (0..nv + 1).map(|_| Vec::new()).collect();
let mut q: Vec<Vec<E::ScalarField>> = (0..nv + 1).map(|_| Vec::new()).collect();

r[nv] = polynomial.to_evaluations();

let mut thread_handles = vec![];

for i in 0..nv {
let k = nv - i;
let point_at_k = point[i];
q[k] = (0..(1 << (k - 1)))
.map(|_| E::ScalarField::zero())
.collect();
r[k - 1] = (0..(1 << (k - 1)))
.map(|_| E::ScalarField::zero())
.collect();
for b in 0..(1 << (k - 1)) {
q[k][b] = r[k][(b << 1) + 1] - &r[k][b << 1];
r[k - 1][b] = r[k][b << 1] * &(E::ScalarField::one() - &point_at_k)
+ &(r[k][(b << 1) + 1] * &point_at_k);
}
let scalars: Vec<_> = (0..(1 << k))
.map(|x| q[k][x >> 1]) // fine
.collect();
let pg = ck.powers_of_g[i].clone();
thread_handles.push(thread::spawn(move || {
<E::G1 as VariableBaseMSM>::msm(&pg, &scalars[..])
.unwrap()
.into_affine()
}));
}

Proof { proofs }
let proofs = thread_handles
.into_iter()
.map(|g| g.join().unwrap())
.collect();

ProofG1 { proofs: proofs }
}

/// Verifies that `value` is the evaluation at `x` of the polynomial
/// committed inside `comm`.
pub fn check_2<'a>(
vk: &VerifierKey<E>,
commitment: &CommitmentG2<E>,
point: &[E::ScalarField],
value: E::ScalarField,
proof: &ProofG1<E>,
) -> bool {
let left = E::pairing(vk.g, commitment.h_product.into_group() - &vk.h.mul(value));

let scalar_size = <E::ScalarField as PrimeField>::MODULUS_BIT_SIZE;
let window_size = FixedBase::get_mul_window_size(vk.nv);

let h_table =
FixedBase::get_window_table(scalar_size as usize, window_size, vk.h.into_group());
let h_mul: Vec<E::G2> = FixedBase::msm(scalar_size as usize, window_size, &h_table, point);

let pairing_rights: Vec<_> = (0..vk.nv)
.into_iter()
.map(|i| vk.h_mask_random[i].into_group() - &h_mul[i])
.collect();
let pairing_rights: Vec<E::G2Prepared> = E::G2::normalize_batch(&pairing_rights)
.into_par_iter()
.map(|p| E::G2Prepared::from(p))
.collect();
let pairing_lefts: Vec<E::G1Prepared> = proof
.proofs
.iter()
.map(|p| E::G1Prepared::from(p))
.collect();

let right = E::multi_pairing(pairing_lefts, pairing_rights);

left == right
}

/// Check a polynomial opening proof in G2 and commitment on G1
pub fn check<'a>(
vk: &VerifierKey<E>,
commitment: &Commitment<E>,
Expand All @@ -206,17 +327,18 @@ impl<E: Pairing> MultilinearPC<E> {
let g_mul: Vec<E::G1> = FixedBase::msm(scalar_size, window_size, &g_table, point);

let pairing_lefts: Vec<_> = (0..vk.nv)
.into_par_iter()
.map(|i| vk.g_mask_random[i].into_group() - &g_mul[i])
.collect();
let pairing_lefts: Vec<E::G1Affine> = E::G1::normalize_batch(&pairing_lefts);
let pairing_lefts: Vec<E::G1Prepared> = pairing_lefts
.into_iter()
.into_par_iter()
.map(|x| E::G1Prepared::from(x))
.collect();

let pairing_rights: Vec<E::G2Prepared> = proof
.proofs
.iter()
.par_iter()
.map(|x| E::G2Prepared::from(*x))
.collect();

Expand All @@ -241,7 +363,7 @@ fn remove_dummy_variable<F: Field>(poly: &[F], pad: usize) -> Vec<F> {
/// generate eq(t,x), a product of multilinear polynomials with fixed t.
/// eq(a,b) is takes extensions of a,b in {0,1}^num_vars such that if a and b in {0,1}^num_vars are equal
/// then this polynomial evaluates to 1.
fn eq_extension<F: Field>(t: &[F]) -> Vec<DenseMultilinearExtension<F>> {
pub fn eq_extension<F: Field>(t: &[F]) -> Vec<DenseMultilinearExtension<F>> {
let dim = t.len();
let mut result = Vec::new();
for i in 0..dim {
Expand Down Expand Up @@ -289,6 +411,34 @@ mod tests {
assert!(result);
}

fn test_polynomial_g2<R: RngCore>(
uni_params: &UniversalParams<E>,
poly: &impl MultilinearExtension<Fr>,
rng: &mut R,
) {
let nv = poly.num_vars();
assert_ne!(nv, 0);
let (ck, vk) = MultilinearPC::<E>::trim(&uni_params, nv);
let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
let com = MultilinearPC::commit_g2(&ck, poly);
let proof = MultilinearPC::open_g1(&ck, poly, &point);

let value = poly.evaluate(&point).unwrap();
let result = MultilinearPC::check_2(&vk, &com, &point, value, &proof);
assert!(result);
}

#[test]
fn test_poly() {
let mut rng = test_rng();

// normal polynomials
let uni_params = MultilinearPC::setup(2, &mut rng);

let poly1 = DenseMultilinearExtension::rand(2, &mut rng);
test_polynomial_g2(&uni_params, &poly1, &mut rng);
}

#[test]
fn setup_commit_verify_correct_polynomials() {
let mut rng = test_rng();
Expand Down