Skip to content

Commit

Permalink
Fold the non_hiding_kzg module in relevant files (#328)
Browse files Browse the repository at this point in the history
* Revert "feat: Implement batch operations in non_hiding_kzg module (#269)"

This reverts commit db4375f.

* refactor: remove non_hiding_kzg module, split code where dependents need it

- Moved `UniversalParams` and several dependent structures for the KZG10 scheme in the `kzg_commitment.rs` file.
- Deleted the `non_hiding_kzg.rs` file,
- Consolidated KZG related structs under the `kzg_commitment` module,
- Updated `mod.rs` to reflect the removal of the `non_hiding_kzg` module.
  • Loading branch information
huitseeker authored Feb 19, 2024
1 parent 5bdc0ee commit e1a69d4
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 565 deletions.
39 changes: 27 additions & 12 deletions src/provider/hyperkzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
use crate::{
errors::NovaError,
provider::{
kzg_commitment::KZGCommitmentEngine,
non_hiding_kzg::{trim, KZGProverKey, KZGVerifierKey, UniversalKZGParam},
kzg_commitment::{KZGCommitmentEngine, KZGProverKey, KZGVerifierKey, UniversalKZGParam},
pedersen::Commitment,
traits::DlogGroup,
util::iterators::DoubleEndedIteratorExt as _,
Expand Down Expand Up @@ -127,7 +126,7 @@ where

fn setup(ck: Arc<UniversalKZGParam<E>>) -> (Self::ProverKey, Self::VerifierKey) {
let len = ck.length() - 1;
trim(ck, len)
UniversalKZGParam::trim(ck, len)
}

fn prove(
Expand Down Expand Up @@ -437,36 +436,52 @@ mod tests {
let ck: CommitmentKey<NE> =
<KZGCommitmentEngine<E> as CommitmentEngineTrait<NE>>::setup(b"test", n);
let ck = Arc::new(ck);
let (pk, _vk): (KZGProverKey<E>, KZGVerifierKey<E>) =
let (pk, vk): (KZGProverKey<E>, KZGVerifierKey<E>) =
EvaluationEngine::<E, NE>::setup(ck.clone());

// poly is in eval. representation; evaluated at [(0,0), (0,1), (1,0), (1,1)]
let poly = vec![Fr::from(1), Fr::from(2), Fr::from(2), Fr::from(4)];

let C = <KZGCommitmentEngine<E> as CommitmentEngineTrait<NE>>::commit(&ck, &poly);
let mut tr = Keccak256Transcript::<NE>::new(b"TestEval");

// Call the prover with a (point, eval) pair. The prover recomputes
// poly(point) = eval', and fails if eval' != eval
let test_inner = |point: Vec<Fr>, eval: Fr| -> Result<(), NovaError> {
let mut tr = Keccak256Transcript::<NE>::new(b"TestEval");
let proof =
EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).unwrap();
let mut tr = Keccak256Transcript::new(b"TestEval");
EvaluationEngine::<E, NE>::verify(&vk, &mut tr, &C, &point, &eval, &proof)
};

// Call the prover with a (point, eval) pair.
// The prover does not recompute so it may produce a proof, but it should not verify
let point = vec![Fr::from(0), Fr::from(0)];
let eval = Fr::ONE;
assert!(EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok());
assert!(test_inner(point, eval).is_ok());

let point = vec![Fr::from(0), Fr::from(1)];
let eval = Fr::from(2);
assert!(EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok());
assert!(test_inner(point, eval).is_ok());

let point = vec![Fr::from(1), Fr::from(1)];
let eval = Fr::from(4);
assert!(EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok());
assert!(test_inner(point, eval).is_ok());

let point = vec![Fr::from(0), Fr::from(2)];
let eval = Fr::from(3);
assert!(EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok());
assert!(test_inner(point, eval).is_ok());

let point = vec![Fr::from(2), Fr::from(2)];
let eval = Fr::from(9);
assert!(EvaluationEngine::<E, NE>::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok());
assert!(test_inner(point, eval).is_ok());

// Try a couple incorrect evaluations and expect failure
let point = vec![Fr::from(2), Fr::from(2)];
let eval = Fr::from(50);
assert!(test_inner(point, eval).is_err());

let point = vec![Fr::from(0), Fr::from(2)];
let eval = Fr::from(4);
assert!(test_inner(point, eval).is_err());
}

#[test]
Expand Down
217 changes: 206 additions & 11 deletions src/provider/kzg_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,218 @@

use std::marker::PhantomData;

use ff::PrimeFieldBits;
use group::{prime::PrimeCurveAffine, Curve};
use abomonation_derive::Abomonation;
use ff::{Field, PrimeField, PrimeFieldBits};
use group::{prime::PrimeCurveAffine, Curve, Group as _};
use pairing::Engine;
use rand::rngs::StdRng;
use rand_core::SeedableRng;
use rand_core::{CryptoRng, RngCore, SeedableRng};
use serde::{Deserialize, Serialize};
use std::sync::Arc;

use crate::traits::{
commitment::{CommitmentEngineTrait, Len},
Engine as NovaEngine, Group,
use crate::provider::pedersen::Commitment;
use crate::provider::traits::DlogGroup;
use crate::provider::util::fb_msm;
use crate::{
digest::SimpleDigestible,
traits::{
commitment::{CommitmentEngineTrait, Len},
Engine as NovaEngine, Group, TranscriptReprTrait,
},
};

use crate::provider::{
non_hiding_kzg::{UVKZGCommitment, UniversalKZGParam},
pedersen::Commitment,
traits::DlogGroup,
};
/// `UniversalParams` are the universal parameters for the KZG10 scheme.
#[derive(Debug, Clone, Eq, Serialize, Deserialize, Abomonation)]
#[serde(bound(
serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize",
deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>"
))]
#[abomonation_omit_bounds]
pub struct UniversalKZGParam<E: Engine> {
/// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to
/// `degree`.
// this is a hack; we just assume the size of the element.
// Look for the static assertions in provider macros for a justification
#[abomonate_with(Vec<[u64; 8]>)]
pub powers_of_g: Vec<E::G1Affine>,
/// Group elements of the form `{ β^i H }`, where `i` ranges from 0 to
/// `degree`.
// this is a hack; we just assume the size of the element.
// Look for the static assertions in provider macros for a justification
#[abomonate_with(Vec<[u64; 16]>)]
pub powers_of_h: Vec<E::G2Affine>,
}

impl<E: Engine> PartialEq for UniversalKZGParam<E> {
fn eq(&self, other: &Self) -> bool {
self.powers_of_g == other.powers_of_g && self.powers_of_h == other.powers_of_h
}
}
// for the purpose of the Len trait, we count commitment bases, i.e. G1 elements
impl<E: Engine> Len for UniversalKZGParam<E> {
fn length(&self) -> usize {
self.powers_of_g.len()
}
}

/// `UnivariateProverKey` is used to generate a proof
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KZGProverKey<E: Engine> {
/// generators from the universal parameters
uv_params: Arc<UniversalKZGParam<E>>,
/// offset at which we start reading into the SRS
offset: usize,
/// maximum supported size
supported_size: usize,
}

impl<E: Engine> KZGProverKey<E> {
pub(in crate::provider) fn new(
uv_params: Arc<UniversalKZGParam<E>>,
offset: usize,
supported_size: usize,
) -> Self {
assert!(
uv_params.max_degree() >= offset + supported_size,
"not enough bases (req: {} from offset {}) in the UVKZGParams (length: {})",
supported_size,
offset,
uv_params.max_degree()
);
Self {
uv_params,
offset,
supported_size,
}
}

pub fn powers_of_g(&self) -> &[E::G1Affine] {
&self.uv_params.powers_of_g[self.offset..self.offset + self.supported_size]
}
}

/// `UVKZGVerifierKey` is used to check evaluation proofs for a given
/// commitment.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(bound(serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize",))]
pub struct KZGVerifierKey<E: Engine> {
/// The generator of G1.
pub g: E::G1Affine,
/// The generator of G2.
pub h: E::G2Affine,
/// β times the above generator of G2.
pub beta_h: E::G2Affine,
}

impl<E: Engine> SimpleDigestible for KZGVerifierKey<E>
where
E::G1Affine: Serialize,
E::G2Affine: Serialize,
{
}

impl<E: Engine> UniversalKZGParam<E> {
/// Returns the maximum supported degree
pub fn max_degree(&self) -> usize {
self.powers_of_g.len()
}

/// Trim the universal parameters to specialize the public parameters
/// for univariate polynomials to the given `supported_size`, and
/// returns prover key and verifier key. `supported_size` should
/// be in range `1..params.len()`
///
/// # Panics
/// If `supported_size` is greater than `self.max_degree()`, or `self.max_degree()` is zero.
pub fn trim(ukzg: Arc<Self>, supported_size: usize) -> (KZGProverKey<E>, KZGVerifierKey<E>) {
assert!(ukzg.max_degree() > 0, "max_degree is zero");
let g = ukzg.powers_of_g[0];
let h = ukzg.powers_of_h[0];
let beta_h = ukzg.powers_of_h[1];
let pk = KZGProverKey::new(ukzg, 0, supported_size + 1);
let vk = KZGVerifierKey { g, h, beta_h };
(pk, vk)
}
}

impl<E: Engine> UniversalKZGParam<E>
where
E::Fr: PrimeFieldBits,
{
/// Build SRS for testing.
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
pub fn gen_srs_for_testing<R: RngCore + CryptoRng>(mut rng: &mut R, max_degree: usize) -> Self {
let beta = E::Fr::random(&mut rng);
let g = E::G1::random(&mut rng);
let h = E::G2::random(rng);

let nz_powers_of_beta = (0..=max_degree)
.scan(beta, |acc, _| {
let val = *acc;
*acc *= beta;
Some(val)
})
.collect::<Vec<E::Fr>>();

let window_size = fb_msm::get_mul_window_size(max_degree);
let scalar_bits = E::Fr::NUM_BITS as usize;

let (powers_of_g_projective, powers_of_h_projective) = rayon::join(
|| {
let g_table = fb_msm::get_window_table(scalar_bits, window_size, g);
fb_msm::multi_scalar_mul::<E::G1>(scalar_bits, window_size, &g_table, &nz_powers_of_beta)
},
|| {
let h_table = fb_msm::get_window_table(scalar_bits, window_size, h);
fb_msm::multi_scalar_mul::<E::G2>(scalar_bits, window_size, &h_table, &nz_powers_of_beta)
},
);

let mut powers_of_g = vec![E::G1Affine::identity(); powers_of_g_projective.len()];
let mut powers_of_h = vec![E::G2Affine::identity(); powers_of_h_projective.len()];

rayon::join(
|| E::G1::batch_normalize(&powers_of_g_projective, &mut powers_of_g),
|| E::G2::batch_normalize(&powers_of_h_projective, &mut powers_of_h),
);

Self {
powers_of_g,
powers_of_h,
}
}
}

/// Commitments
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)]
#[serde(bound(
serialize = "E::G1Affine: Serialize",
deserialize = "E::G1Affine: Deserialize<'de>"
))]
pub struct UVKZGCommitment<E: Engine>(
/// the actual commitment is an affine point.
pub E::G1Affine,
);

impl<E: Engine> TranscriptReprTrait<E::G1> for UVKZGCommitment<E>
where
E::G1: DlogGroup,
// Note: due to the move of the bound TranscriptReprTrait<G> on G::Base from Group to Engine
<E::G1 as Group>::Base: TranscriptReprTrait<E::G1>,
{
fn to_transcript_bytes(&self) -> Vec<u8> {
// TODO: avoid the round-trip through the group (to_curve .. to_coordinates)
let (x, y, is_infinity) = self.0.to_curve().to_coordinates();
let is_infinity_byte = (!is_infinity).into();
[
x.to_transcript_bytes(),
y.to_transcript_bytes(),
[is_infinity_byte].to_vec(),
]
.concat()
}
}

/// Provides a commitment engine
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down
1 change: 0 additions & 1 deletion src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pub(crate) mod secp_secq;
pub(crate) mod traits;
// a non-hiding variant of {kzg, zeromorph}
mod kzg_commitment;
mod non_hiding_kzg;
pub(crate) mod util;

// crate-private modules
Expand Down
Loading

1 comment on commit e1a69d4

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

Benchmarks

Table of Contents

Overview

This benchmark report shows the Arecibo GPU benchmarks.
NVIDIA L4
Intel(R) Xeon(R) CPU @ 2.20GHz
32 vCPUs
125 GB RAM
Workflow run: https://github.com/lurk-lab/arecibo/actions/runs/7962962052

Benchmark Results

RecursiveSNARK-NIVC-2

ref=5bdc0ee ref=e1a69d4
Prove-NumCons-6540 44.47 ms (✅ 1.00x) 44.46 ms (✅ 1.00x faster)
Verify-NumCons-6540 34.18 ms (✅ 1.00x) 34.08 ms (✅ 1.00x faster)
Prove-NumCons-1028888 319.81 ms (✅ 1.00x) 326.91 ms (✅ 1.02x slower)
Verify-NumCons-1028888 250.88 ms (✅ 1.00x) 248.14 ms (✅ 1.01x faster)

CompressedSNARK-NIVC-Commitments-2

ref=5bdc0ee ref=e1a69d4
Prove-NumCons-6540 10.33 s (✅ 1.00x) 10.46 s (✅ 1.01x slower)
Verify-NumCons-6540 48.17 ms (✅ 1.00x) 48.02 ms (✅ 1.00x faster)
Prove-NumCons-1028888 52.11 s (✅ 1.00x) 52.71 s (✅ 1.01x slower)
Verify-NumCons-1028888 47.71 ms (✅ 1.00x) 47.92 ms (✅ 1.00x slower)

Made with criterion-table

Please sign in to comment.