Skip to content
Open
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
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>,
}
199 changes: 180 additions & 19 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_iter()
.map(|_| E::ScalarField::zero())
.collect();
r[k - 1] = (0..(1 << (k - 1)))
.into_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_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
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_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,6 +327,7 @@ 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_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);
Expand Down Expand Up @@ -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,45 @@ 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_commit_on_g2_short() {
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 test_commit_on_g2_long() {
let mut rng = test_rng();

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

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

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