Skip to content

Commit f579363

Browse files
author
Colin Roberts
committed
WIP: commitment docs
1 parent 7d76948 commit f579363

File tree

4 files changed

+198
-72
lines changed

4 files changed

+198
-72
lines changed

folding-schemes/src/commitment/ipa.rs

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
/// IPA implements the modified Inner Product Argument described in
2-
/// [Halo](https://eprint.iacr.org/2019/1021.pdf). The variable names used follow the paper
3-
/// notation in order to make it more readable.
4-
///
5-
/// The implementation does the following optimizations in order to reduce the amount of
6-
/// constraints in the circuit:
7-
/// i. <s, b> computation is done in log time following a modification of the equation 3 in section
8-
/// 3.2 from the paper.
9-
/// ii. s computation is done in 2^{k+1}-2 instead of k*2^k.
1+
//! IPA implements the modified Inner Product Argument described in
2+
//! [Halo](https://eprint.iacr.org/2019/1021.pdf). The variable names used follow the paper
3+
//! notation in order to make it more readable.
4+
//!
5+
//! The implementation does the following optimizations in order to reduce the amount of
6+
//! constraints in the circuit:
7+
//! i. <s, b> computation is done in log time following a modification of the equation 3 in section
8+
//! 3.2 from the paper.
9+
//! ii. s computation is done in 2^{k+1}-2 instead of k*2^k.
10+
1011
use ark_ec::{AffineRepr, CurveGroup};
1112
use ark_ff::{Field, PrimeField};
1213
use ark_r1cs_std::{
@@ -30,19 +31,32 @@ use crate::utils::{
3031
};
3132
use crate::Error;
3233

34+
/// IPA proof structure containing all components needed for verification
3335
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
3436
pub struct Proof<C: CurveGroup> {
35-
a: C::ScalarField,
36-
l: Vec<C::ScalarField>,
37-
r: Vec<C::ScalarField>,
38-
L: Vec<C>,
39-
R: Vec<C>,
37+
/// Final value of vector a after the folding process
38+
pub a: C::ScalarField,
39+
/// Left blinding factors used in each round
40+
pub l: Vec<C::ScalarField>,
41+
/// Right blinding factors used in each round
42+
pub r: Vec<C::ScalarField>,
43+
/// Left commitments for each round
44+
pub L: Vec<C>,
45+
/// Right commitments for each round
46+
pub R: Vec<C>,
4047
}
4148

42-
/// IPA implements the Inner Product Argument protocol following the CommitmentScheme trait. The
43-
/// `H` parameter indicates if to use the commitment in hiding mode or not.
49+
/// Implementation of the Inner Product Argument (IPA) as described in [Halo](https://eprint.iacr.org/2019/1021.pdf).
50+
/// The variable names follow the paper notation to maintain clarity and readability.
51+
///
52+
/// This implementation includes optimizations to reduce circuit constraints:
53+
/// 1. The `<s, b>` computation is done in log time using a modified version of equation 3 in section 3.2
54+
/// 2. The `s` computation is optimized to take 2^{k+1}-2 steps instead of k*2^k steps
55+
///
56+
/// The `H` parameter indicates if to use the commitment in hiding mode or not.
4457
#[derive(Debug, Clone, Eq, PartialEq)]
4558
pub struct IPA<C: CurveGroup, const H: bool = false> {
59+
/// The inner [`CurveGroup`] type
4660
_c: PhantomData<C>,
4761
}
4862

@@ -54,13 +68,6 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
5468
type ProverChallenge = (C::ScalarField, C, Vec<C::ScalarField>);
5569
type Challenge = (C::ScalarField, C, Vec<C::ScalarField>);
5670

57-
fn is_hiding() -> bool {
58-
if H {
59-
return true;
60-
}
61-
false
62-
}
63-
6471
fn setup(
6572
mut rng: impl RngCore,
6673
len: usize,
@@ -110,6 +117,7 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
110117
return Err(Error::BlindingNotZero);
111118
}
112119
let d = a.len();
120+
// TODO (autoparallel): Casting this back into `usize` could be dangerous in 32bit systems (e.g., wasm32)
113121
let k = (f64::from(d as u32).log2()) as usize;
114122

115123
if params.generators.len() < a.len() {
@@ -202,8 +210,8 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
202210
Ok((
203211
Proof {
204212
a: a[0],
205-
l: l.clone(),
206-
r: r.clone(),
213+
l,
214+
r,
207215
L,
208216
R,
209217
},
@@ -284,9 +292,10 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
284292
}
285293

286294
// compute b & G from s
287-
let s = build_s(&u, &u_invs, k)?;
295+
let s = build_s(&u, &u_invs, k);
288296
// b = <s, b_vec> = <s, [1, x, x^2, ..., x^d-1]>
289297
let b = s_b_inner(&u, &x)?;
298+
// TODO (autoparallel): Casting this back into `usize` could be dangerous in 32bit systems (e.g., wasm32)
290299
let d: usize = 2_u64.pow(k as u32) as usize;
291300
if params.generators.len() < d {
292301
return Err(Error::PedersenParamsLen(params.generators.len(), d));
@@ -316,7 +325,10 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
316325
}
317326
}
318327

319-
/// Computes s such that
328+
/// Computes the s vector used in IPA verification
329+
///
330+
/// The resulting s vector has the form:
331+
/// ```text
320332
/// s = (
321333
/// u₁⁻¹ u₂⁻¹ … uₖ⁻¹,
322334
/// u₁ u₂⁻¹ … uₖ⁻¹,
@@ -325,10 +337,15 @@ impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> {
325337
/// ⋮ ⋮ ⋮
326338
/// u₁ u₂ … uₖ
327339
/// )
328-
/// Uses Halo2 approach computing $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} X^{2^i})$,
329-
/// taking 2^{k+1}-2.
330-
/// src: https://github.com/zcash/halo2/blob/81729eca91ba4755e247f49c3a72a4232864ec9e/halo2_proofs/src/poly/commitment/verifier.rs#L156
331-
fn build_s<F: PrimeField>(u: &[F], u_invs: &[F], k: usize) -> Result<Vec<F>, Error> {
340+
/// ```
341+
/// Uses the Halo2 approach computing $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} X^{2^i})$,
342+
/// which takes 2^{k+1}-2 steps.
343+
/// src: <https://github.com/zcash/halo2/blob/81729eca91ba4755e247f49c3a72a4232864ec9e/halo2_proofs/src/poly/commitment/verifier.rs#L156>
344+
///
345+
/// # Errors
346+
///
347+
/// Returns an error if the vector construction fails.
348+
fn build_s<F: PrimeField>(u: &[F], u_invs: &[F], k: usize) -> Vec<F> {
332349
let d: usize = 2_u64.pow(k as u32) as usize;
333350
let mut s: Vec<F> = vec![F::one(); d];
334351
for (len, (u_j, u_j_inv)) in u
@@ -347,7 +364,7 @@ fn build_s<F: PrimeField>(u: &[F], u_invs: &[F], k: usize) -> Result<Vec<F>, Err
347364
*s *= u_j;
348365
}
349366
}
350-
Ok(s)
367+
s
351368
}
352369

353370
/// Computes (in-circuit) s such that
@@ -388,6 +405,12 @@ fn build_s_gadget<F: PrimeField, CF: PrimeField>(
388405
Ok(s)
389406
}
390407

408+
/// Computes the inner product of two vectors
409+
///
410+
/// # Errors
411+
///
412+
/// Returns an error if:
413+
/// - The vectors have different lengths
391414
fn inner_prod<F: PrimeField>(a: &[F], b: &[F]) -> Result<F, Error> {
392415
if a.len() != b.len() {
393416
return Err(Error::NotSameLength(
@@ -404,12 +427,19 @@ fn inner_prod<F: PrimeField>(a: &[F], b: &[F]) -> Result<F, Error> {
404427
Ok(c)
405428
}
406429

407-
// g(x, u_1, u_2, ..., u_k) = <s, b>, naively takes linear, but can compute in log time through
408-
// g(x, u_1, u_2, ..., u_k) = \Prod u_i x^{2^i} + u_i^-1
430+
/// Computes g(x, u_1, u_2, ..., u_k) = <s, b> efficiently in log time
431+
///
432+
/// Rather than computing naively, this uses the formula:
433+
/// g(x, u_1, u_2, ..., u_k) = \Prod u_i x^{2^i} + u_i^-1
434+
///
435+
/// # Errors
436+
///
437+
/// Returns an error if:
438+
/// - Computing any inverse fails
409439
fn s_b_inner<F: PrimeField>(u: &[F], x: &F) -> Result<F, Error> {
410440
let mut c: F = F::one();
411441
let mut x_2_i = *x; // x_2_i is x^{2^i}, starting from x^{2^0}=x
412-
for u_i in u.iter() {
442+
for u_i in u {
413443
c *= (*u_i * x_2_i)
414444
+ u_i
415445
.inverse()
@@ -427,7 +457,7 @@ fn s_b_inner_gadget<F: PrimeField, CF: PrimeField>(
427457
) -> Result<EmulatedFpVar<F, CF>, SynthesisError> {
428458
let mut c: EmulatedFpVar<F, CF> = EmulatedFpVar::<F, CF>::one();
429459
let mut x_2_i = x.clone(); // x_2_i is x^{2^i}, starting from x^{2^0}=x
430-
for u_i in u.iter() {
460+
for u_i in u {
431461
c *= u_i.clone() * x_2_i.clone() + u_i.inverse()?;
432462
x_2_i *= x_2_i.clone();
433463
}
@@ -467,16 +497,17 @@ where
467497
let r: Vec<EmulatedFpVar<C::ScalarField, CF<C>>> =
468498
Vec::new_variable(cs.clone(), || Ok(val.borrow().r.clone()), mode)?;
469499
let L: Vec<GC> = Vec::new_variable(cs.clone(), || Ok(val.borrow().L.clone()), mode)?;
470-
let R: Vec<GC> = Vec::new_variable(cs.clone(), || Ok(val.borrow().R.clone()), mode)?;
500+
let R: Vec<GC> = Vec::new_variable(cs, || Ok(val.borrow().R.clone()), mode)?;
471501

472502
Ok(Self { a, l, r, L, R })
473503
})
474504
}
475505
}
476506

477-
/// IPAGadget implements the circuit that verifies an IPA Proof. The `H` parameter indicates if to
478-
/// use the commitment in hiding mode or not, reducing a bit the number of constraints needed in
479-
/// the later case.
507+
/// In-circuit IPA verification gadget implementation
508+
///
509+
/// Provides constraint generation for verifying IPA proofs. The `H` parameter indicates if the
510+
/// commitment is in hiding mode, which affects the number of constraints needed.
480511
pub struct IPAGadget<C, GC, const H: bool = false>
481512
where
482513
C: CurveGroup,
@@ -574,16 +605,17 @@ mod tests {
574605

575606
#[test]
576607
fn test_ipa() -> Result<(), Error> {
577-
let _ = test_ipa_opt::<false>()?;
578-
let _ = test_ipa_opt::<true>()?;
608+
test_ipa_opt::<false>()?;
609+
test_ipa_opt::<true>()?;
579610
Ok(())
580611
}
581612
fn test_ipa_opt<const hiding: bool>() -> Result<(), Error> {
582-
let mut rng = ark_std::test_rng();
583-
584613
const k: usize = 4;
614+
// TODO (autoparallel): Casting into `usize` may be dangerous on 32bit systems (e.g., wasm32)
585615
const d: usize = 2_u64.pow(k as u32) as usize;
586616

617+
let mut rng = ark_std::test_rng();
618+
587619
// setup params
588620
let (params, _) = IPA::<Projective, hiding>::setup(&mut rng, d)?;
589621

@@ -619,16 +651,17 @@ mod tests {
619651

620652
#[test]
621653
fn test_ipa_gadget() -> Result<(), Error> {
622-
let _ = test_ipa_gadget_opt::<false>()?;
623-
let _ = test_ipa_gadget_opt::<true>()?;
654+
test_ipa_gadget_opt::<false>()?;
655+
test_ipa_gadget_opt::<true>()?;
624656
Ok(())
625657
}
626658
fn test_ipa_gadget_opt<const hiding: bool>() -> Result<(), Error> {
627-
let mut rng = ark_std::test_rng();
628-
629659
const k: usize = 3;
660+
// TODO (autoparallel): Casting into `usize` may be dangerous on 32bit systems (e.g., wasm32)
630661
const d: usize = 2_u64.pow(k as u32) as usize;
631662

663+
let mut rng = ark_std::test_rng();
664+
632665
// setup params
633666
let (params, _) = IPA::<Projective, hiding>::setup(&mut rng, d)?;
634667

folding-schemes/src/commitment/kzg.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,6 @@ where
9393
type ProverChallenge = E::ScalarField;
9494
type Challenge = E::ScalarField;
9595

96-
fn is_hiding() -> bool {
97-
if H {
98-
return true;
99-
}
100-
false
101-
}
102-
10396
/// setup returns the tuple (ProverKey, VerifierKey). For real world deployments the setup must
10497
/// be computed in the most trustless way possible, usually through a MPC ceremony.
10598
fn setup(

0 commit comments

Comments
 (0)