sonobe/folding-schemes/src/commitment/kzg.rs
folding-schemes/src/commitment/kzg.rsRaw
1/// Adaptation of the prover methods and structs from arkworks/poly-commit's KZG10 implementation
2/// into the CommitmentProver trait.
3///
4/// The motivation to do so, is that we want to be able to use KZG / Pedersen for committing to
5/// vectors indistinctly, and the arkworks KZG10 implementation contains all the methods under the
6/// same trait, which requires the Pairing trait, where the prover does not need access to the
7/// Pairing but only to G1.
8/// For our case, we want the folding schemes prover to be agnostic to pairings, since in the
9/// non-ethereum cases we may use non-pairing-friendly curves with Pedersen commitments, so the
10/// trait & types that we use should not depend on the Pairing type for the prover. Therefore, we
11/// separate the CommitmentSchemeProver from the setup and verify phases, so the prover can be
12/// defined without depending on pairings.
13use ark_ec::{pairing::Pairing, CurveGroup, VariableBaseMSM};
14use ark_ff::PrimeField;
15use ark_poly::{
16 univariate::{DenseOrSparsePolynomial, DensePolynomial},
17 DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial,
18};
19use ark_poly_commit::kzg10::{VerifierKey, KZG10};
20use ark_std::rand::{Rng, RngCore};
21use ark_std::{borrow::Cow, fmt::Debug};
22use ark_std::{One, Zero};
23use core::marker::PhantomData;
24use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
25
26use super::CommitmentProver;
27use crate::transcript::Transcript;
28use crate::Error;
29
30/// ProverKey defines a similar struct as in ark_poly_commit::kzg10::Powers, but instead of
31/// depending on the Pairing trait it depends on the CurveGroup trait.
32#[derive(Debug, Clone, Default, Eq, PartialEq)]
33pub struct ProverKey<'a, C: CurveGroup> {
34 /// Group elements of the form `β^i G`, for different values of `i`.
35 pub powers_of_g: Cow<'a, [C::Affine]>,
36}
37
38#[derive(Debug)]
39pub struct KZGSetup<P: Pairing> {
40 _p: PhantomData<P>,
41}
42impl<'a, P> KZGSetup<P>
43where
44 P: Pairing,
45{
46 /// setup returns the tuple (ProverKey, VerifierKey). For real world deployments the setup must
47 /// be computed in the most trustless way possible, usually through a MPC ceremony.
48 pub fn setup<R: Rng>(rng: &mut R, len: usize) -> (ProverKey<'a, P::G1>, VerifierKey<P>) {
49 let len = len.next_power_of_two();
50 let universal_params = KZG10::<P, DensePolynomial<P::ScalarField>>::setup(len, false, rng)
51 .expect("Setup failed");
52 let powers_of_g = universal_params.powers_of_g[..=len].to_vec();
53 let powers = ProverKey::<P::G1> {
54 powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g),
55 };
56 let vk = VerifierKey {
57 g: universal_params.powers_of_g[0],
58 gamma_g: universal_params.powers_of_gamma_g[&0],
59 h: universal_params.h,
60 beta_h: universal_params.beta_h,
61 prepared_h: universal_params.prepared_h.clone(),
62 prepared_beta_h: universal_params.prepared_beta_h.clone(),
63 };
64 (powers, vk)
65 }
66}
67
68/// KZGProver implements the CommitmentProver trait for the KZG commitment scheme.
69#[derive(Debug, Clone, Default, Eq, PartialEq)]
70pub struct KZGProver<'a, C: CurveGroup, const H: bool = false> {
71 _a: PhantomData<&'a ()>,
72 _c: PhantomData<C>,
73}
74impl<'a, C, const H: bool> CommitmentProver<C, H> for KZGProver<'a, C, H>
75where
76 C: CurveGroup,
77{
78 type Params = ProverKey<'a, C>;
79 /// Proof is a tuple containing (evaluation, proof)
80 type Proof = (C::ScalarField, C);
81
82 /// commit implements the CommitmentProver commit interface, adapting the implementation from
83 /// https://github.com/arkworks-rs/poly-commit/tree/c724fa666e935bbba8db5a1421603bab542e15ab/poly-commit/src/kzg10/mod.rs#L178
84 /// with the main difference being the removal of the blinding factors and the no-dependency to
85 /// the Pairing trait.
86 fn commit(
87 params: &Self::Params,
88 v: &[C::ScalarField],
89 _blind: &C::ScalarField,
90 ) -> Result<C, Error> {
91 if !_blind.is_zero() || H {
92 return Err(Error::NotSupportedYet("hiding".to_string()));
93 }
94
95 let polynomial = poly_from_vec(v.to_vec())?;
96 check_degree_is_too_large(polynomial.degree(), params.powers_of_g.len())?;
97
98 let (num_leading_zeros, plain_coeffs) =
99 skip_first_zero_coeffs_and_convert_to_bigints(&polynomial);
100 let commitment = <C as VariableBaseMSM>::msm_bigint(
101 ¶ms.powers_of_g[num_leading_zeros..],
102 &plain_coeffs,
103 );
104 Ok(commitment)
105 }
106
107 /// prove implements the CommitmentProver prove interface, adapting the implementation from
108 /// https://github.com/arkworks-rs/poly-commit/tree/c724fa666e935bbba8db5a1421603bab542e15ab/poly-commit/src/kzg10/mod.rs#L307
109 /// with the main difference being the removal of the blinding factors and the no-dependency to
110 /// the Pairing trait.
111 fn prove(
112 params: &Self::Params,
113 transcript: &mut impl Transcript<C>,
114 cm: &C,
115 v: &[C::ScalarField],
116 _blind: &C::ScalarField,
117 _rng: Option<&mut dyn RngCore>,
118 ) -> Result<Self::Proof, Error> {
119 if !_blind.is_zero() || H {
120 return Err(Error::NotSupportedYet("hiding".to_string()));
121 }
122
123 let polynomial = poly_from_vec(v.to_vec())?;
124 check_degree_is_too_large(polynomial.degree(), params.powers_of_g.len())?;
125
126 transcript.absorb_point(cm)?;
127 let challenge = transcript.get_challenge();
128
129 // Compute q(x) = (p(x) - p(z)) / (x-z). Observe that this quotient does not change with z
130 // because p(z) is the remainder term. We can therefore omit p(z) when computing the
131 // quotient.
132 let divisor = DensePolynomial::<C::ScalarField>::from_coefficients_vec(vec![
133 -challenge,
134 C::ScalarField::one(),
135 ]);
136 let (witness_poly, remainder_poly) = DenseOrSparsePolynomial::from(&polynomial)
137 .divide_with_q_and_r(&DenseOrSparsePolynomial::from(&divisor))
138 // the panic inside `divide_with_q_and_r` should never be reached, since the divisor
139 // polynomial is constructed right before and is set to not be zero. And the `.unwrap`
140 // should not give an error.
141 .unwrap();
142 let evaluation = remainder_poly[0];
143
144 check_degree_is_too_large(witness_poly.degree(), params.powers_of_g.len())?;
145 let (num_leading_zeros, witness_coeffs) =
146 skip_first_zero_coeffs_and_convert_to_bigints(&witness_poly);
147 let proof = <C as VariableBaseMSM>::msm_bigint(
148 ¶ms.powers_of_g[num_leading_zeros..],
149 &witness_coeffs,
150 );
151
152 Ok((evaluation, proof))
153 }
154}
155
156/// returns the interpolated polynomial of degree=v.len().next_power_of_two(), which passes through all
157/// the given elements of v.
158fn poly_from_vec<F: PrimeField>(v: Vec<F>) -> Result<DensePolynomial<F>, Error> {
159 let D = GeneralEvaluationDomain::<F>::new(v.len()).ok_or(Error::NewDomainFail)?;
160 Ok(Evaluations::from_vec_and_domain(v, D).interpolate())
161}
162
163fn check_degree_is_too_large(
164 degree: usize,
165 num_powers: usize,
166) -> Result<(), ark_poly_commit::error::Error> {
167 let num_coefficients = degree + 1;
168 if num_coefficients > num_powers {
169 Err(ark_poly_commit::error::Error::TooManyCoefficients {
170 num_coefficients,
171 num_powers,
172 })
173 } else {
174 Ok(())
175 }
176}
177
178fn skip_first_zero_coeffs_and_convert_to_bigints<F: PrimeField, P: DenseUVPolynomial<F>>(
179 p: &P,
180) -> (usize, Vec<F::BigInt>) {
181 let mut num_leading_zeros = 0;
182 while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() {
183 num_leading_zeros += 1;
184 }
185 let coeffs = convert_to_bigints(&p.coeffs()[num_leading_zeros..]);
186 (num_leading_zeros, coeffs)
187}
188
189fn convert_to_bigints<F: PrimeField>(p: &[F]) -> Vec<F::BigInt> {
190 ark_std::cfg_iter!(p)
191 .map(|s| s.into_bigint())
192 .collect::<Vec<_>>()
193}
194
195#[cfg(test)]
196mod tests {
197 use ark_bn254::{Bn254, Fr, G1Projective as G1};
198 use ark_poly_commit::kzg10::{Commitment as KZG10Commitment, Proof as KZG10Proof, KZG10};
199 use ark_std::{test_rng, UniformRand};
200
201 use super::*;
202 use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript};
203
204 #[test]
205 fn test_kzg_commitment_scheme() {
206 let rng = &mut test_rng();
207 let poseidon_config = poseidon_test_config::<Fr>();
208 let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
209 let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
210
211 let n = 10;
212 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(rng, n);
213
214 let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(rng)).take(n).collect();
215 let cm = KZGProver::<G1>::commit(&pk, &v, &Fr::zero()).unwrap();
216
217 let (eval, proof) =
218 KZGProver::<G1>::prove(&pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap();
219
220 // verify the proof:
221 // get evaluation challenge
222 transcript_v.absorb_point(&cm).unwrap();
223 let challenge = transcript_v.get_challenge();
224 // verify the KZG proof using arkworks method
225 assert!(KZG10::<Bn254, DensePolynomial<Fr>>::check(
226 &vk,
227 &KZG10Commitment(cm.into_affine()),
228 challenge,
229 eval,
230 &KZG10Proof::<Bn254> {
231 w: proof.into_affine(),
232 random_v: None,
233 },
234 )
235 .unwrap());
236 }
237}