diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f4be64a..0238476f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - master env: RUST_BACKTRACE: 1 + RUSTFLAGS: "--emit asm -C llvm-args=-x86-asm-syntax=intel" jobs: style: diff --git a/Cargo.toml b/Cargo.toml index d803afb6..272fe68a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/src/multilinear_pc/data_structures.rs b/src/multilinear_pc/data_structures.rs index 6920ece7..cab12de1 100644 --- a/src/multilinear_pc/data_structures.rs +++ b/src/multilinear_pc/data_structures.rs @@ -23,6 +23,8 @@ pub struct UniversalParams { pub h: E::G2Affine, /// g^randomness pub g_mask: Vec, + /// h^randomness + pub h_mask: Vec, } /// Public Parameter used by prover @@ -51,10 +53,12 @@ pub struct VerifierKey { pub h: E::G2Affine, /// g^t1, g^t2, ... pub g_mask_random: Vec, + /// h^t1, h^t2,... + pub h_mask_random: Vec, } #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -/// commitment +/// PST commitment on the G1 group pub struct Commitment { /// number of variables pub nv: usize, @@ -63,8 +67,24 @@ pub struct Commitment { } #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +/// PST Commitment on the G2 group +pub struct CommitmentG2 { + /// 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 { /// Evaluation of quotients pub proofs: Vec, } + +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)] +/// PST Proof of opening on G1 (so commitment is on G2) +pub struct ProofG1 { + /// Evaluation of quotients + pub proofs: Vec, +} diff --git a/src/multilinear_pc/mod.rs b/src/multilinear_pc/mod.rs index bd5d3e53..8a562f5f 100644 --- a/src/multilinear_pc/mod.rs +++ b/src/multilinear_pc/mod.rs @@ -1,3 +1,5 @@ +use self::data_structures::{CommitmentG2, ProofG1}; + use crate::multilinear_pc::data_structures::{ Commitment, CommitterKey, Proof, UniversalParams, VerifierKey, }; @@ -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; @@ -46,8 +51,8 @@ impl MultilinearPC { 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(); } @@ -94,15 +99,22 @@ impl MultilinearPC { 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, } } @@ -127,6 +139,7 @@ impl MultilinearPC { g: params.g, h: params.h, g_mask_random: (¶ms.g_mask[to_reduce..]).to_vec(), + h_mask_random: (¶ms.h_mask[to_reduce..]).to_vec(), }; (ck, vk) } @@ -137,17 +150,29 @@ impl MultilinearPC { polynomial: &impl MultilinearExtension, ) -> Commitment { let nv = polynomial.num_vars(); - let scalars: Vec<_> = polynomial - .to_evaluations() - .into_iter() - .map(|x| x.into_bigint()) - .collect(); - let g_product = - ::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 = ::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, + polynomial: &impl MultilinearExtension, + ) -> CommitmentG2 { + let nv = polynomial.num_vars(); + let scalars: Vec<_> = polynomial.to_evaluations(); + debug_assert!(scalars.len() == ck.powers_of_h[0].len()); + let h_product = ::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, @@ -161,14 +186,16 @@ impl MultilinearPC { 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)) { @@ -177,19 +204,113 @@ impl MultilinearPC { + &(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 = - ::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 || { + ::msm(&ph, &scalars[..]) + .unwrap() + .into_affine() + })); + } + print!("Waiting for threads to finish..."); + 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, + polynomial: &impl MultilinearExtension, + point: &[E::ScalarField], + ) -> ProofG1 { + assert_eq!(polynomial.num_vars(), ck.nv, "Invalid size of polynomial"); + let nv = polynomial.num_vars(); + let mut r: Vec> = (0..nv + 1).map(|_| Vec::new()).collect(); + let mut q: Vec> = (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 || { + ::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, + commitment: &CommitmentG2, + point: &[E::ScalarField], + value: E::ScalarField, + proof: &ProofG1, + ) -> bool { + let left = E::pairing(vk.g, commitment.h_product.into_group() - &vk.h.mul(value)); + + let scalar_size = ::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 = 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::G2::normalize_batch(&pairing_rights) + .into_iter() + .map(|p| E::G2Prepared::from(p)) + .collect(); + let pairing_lefts: Vec = 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, commitment: &Commitment, @@ -206,6 +327,7 @@ impl MultilinearPC { let g_mul: Vec = 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::G1::normalize_batch(&pairing_lefts); @@ -241,7 +363,7 @@ fn remove_dummy_variable(poly: &[F], pad: usize) -> Vec { /// 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(t: &[F]) -> Vec> { +pub fn eq_extension(t: &[F]) -> Vec> { let dim = t.len(); let mut result = Vec::new(); for i in 0..dim { @@ -289,6 +411,45 @@ mod tests { assert!(result); } + fn test_polynomial_g2( + uni_params: &UniversalParams, + poly: &impl MultilinearExtension, + rng: &mut R, + ) { + let nv = poly.num_vars(); + assert_ne!(nv, 0); + let (ck, vk) = MultilinearPC::::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();