arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/commitment/pedersen.rs
folding-schemes/src/commitment/pedersen.rsRaw
  1use ark_ec::CurveGroup;
  2use ark_ff::Field;
  3use ark_r1cs_std::{boolean::Boolean, groups::GroupOpsBounds, prelude::CurveVar};
  4use ark_relations::r1cs::SynthesisError;
  5use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
  6use ark_std::Zero;
  7use ark_std::{rand::RngCore, UniformRand};
  8use core::marker::PhantomData;
  9
 10use super::CommitmentScheme;
 11use crate::transcript::Transcript;
 12use crate::utils::vec::{vec_add, vec_scalar_mul};
 13use crate::Error;
 14
 15#[derive(Debug, Clone, Eq, PartialEq)]
 16pub struct Proof<C: CurveGroup> {
 17    pub R: C,
 18    pub u: Vec<C::ScalarField>,
 19    pub r_u: C::ScalarField, // blind
 20}
 21
 22#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 23pub struct Params<C: CurveGroup> {
 24    pub h: C,
 25    pub generators: Vec<C::Affine>,
 26}
 27
 28#[derive(Debug, Clone, Eq, PartialEq)]
 29pub struct Pedersen<C: CurveGroup, const H: bool = false> {
 30    _c: PhantomData<C>,
 31}
 32
 33/// Implements the CommitmentScheme trait for Pedersen commitments
 34impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for Pedersen<C, H> {
 35    type ProverParams = Params<C>;
 36    type VerifierParams = Params<C>;
 37    type Proof = Proof<C>;
 38    type ProverChallenge = (C::ScalarField, Vec<C::ScalarField>, C, C::ScalarField);
 39    type Challenge = C::ScalarField;
 40
 41    fn setup(
 42        mut rng: impl RngCore,
 43        len: usize,
 44    ) -> Result<(Self::ProverParams, Self::VerifierParams), Error> {
 45        let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(&mut rng))
 46            .take(len.next_power_of_two())
 47            .collect();
 48        let p = Params::<C> {
 49            h: C::rand(&mut rng),
 50            generators,
 51        };
 52        Ok((p.clone(), p))
 53    }
 54
 55    fn commit(
 56        params: &Self::ProverParams,
 57        v: &[C::ScalarField],
 58        r: &C::ScalarField, // blinding factor
 59    ) -> Result<C, Error> {
 60        if params.generators.len() < v.len() {
 61            return Err(Error::PedersenParamsLen(params.generators.len(), v.len()));
 62        }
 63        if !H && (!r.is_zero()) {
 64            return Err(Error::BlindingNotZero);
 65        }
 66
 67        // h⋅r + <g, v>
 68        // use msm_unchecked because we already ensured at the if that lengths match
 69        if !H {
 70            return Ok(C::msm_unchecked(&params.generators[..v.len()], v));
 71        }
 72        Ok(params.h.mul(r) + C::msm_unchecked(&params.generators[..v.len()], v))
 73    }
 74
 75    fn prove(
 76        params: &Self::ProverParams,
 77        transcript: &mut impl Transcript<C>,
 78        cm: &C,
 79        v: &[C::ScalarField],
 80        r: &C::ScalarField, // blinding factor
 81        _rng: Option<&mut dyn RngCore>,
 82    ) -> Result<Self::Proof, Error> {
 83        transcript.absorb_point(cm)?;
 84        let r1 = transcript.get_challenge();
 85        let d = transcript.get_challenges(v.len());
 86
 87        // R = h⋅r_1 + <g, d>
 88        // use msm_unchecked because we already ensured at the if that lengths match
 89        let mut R: C = C::msm_unchecked(&params.generators[..d.len()], &d);
 90        if H {
 91            R += params.h.mul(r1);
 92        }
 93
 94        transcript.absorb_point(&R)?;
 95        let e = transcript.get_challenge();
 96
 97        let challenge = (r1, d, R, e);
 98        Self::prove_with_challenge(params, challenge, v, r, _rng)
 99    }
100
101    fn prove_with_challenge(
102        params: &Self::ProverParams,
103        challenge: Self::ProverChallenge,
104        v: &[C::ScalarField], // vector
105        r: &C::ScalarField,   // blinding factor
106        _rng: Option<&mut dyn RngCore>,
107    ) -> Result<Self::Proof, Error> {
108        if params.generators.len() < v.len() {
109            return Err(Error::PedersenParamsLen(params.generators.len(), v.len()));
110        }
111        if !H && (!r.is_zero()) {
112            return Err(Error::BlindingNotZero);
113        }
114        let (r1, d, R, e): (C::ScalarField, Vec<C::ScalarField>, C, C::ScalarField) = challenge;
115
116        // u = d + v⋅e
117        let u = vec_add(&vec_scalar_mul(v, &e), &d)?;
118        // r_u = e⋅r + r_1
119        let mut r_u = C::ScalarField::zero();
120        if H {
121            r_u = e * r + r1;
122        }
123
124        Ok(Self::Proof { R, u, r_u })
125    }
126
127    fn verify(
128        params: &Self::VerifierParams,
129        transcript: &mut impl Transcript<C>,
130        cm: &C,
131        proof: &Proof<C>,
132    ) -> Result<(), Error> {
133        transcript.absorb_point(cm)?;
134        transcript.get_challenge(); // r_1
135        transcript.get_challenges(proof.u.len()); // d
136        transcript.absorb_point(&proof.R)?;
137        let e = transcript.get_challenge();
138        Self::verify_with_challenge(params, e, cm, proof)
139    }
140
141    fn verify_with_challenge(
142        params: &Self::VerifierParams,
143        challenge: Self::Challenge,
144        cm: &C,
145        proof: &Proof<C>,
146    ) -> Result<(), Error> {
147        if params.generators.len() < proof.u.len() {
148            return Err(Error::PedersenParamsLen(
149                params.generators.len(),
150                proof.u.len(),
151            ));
152        }
153        if !H && (!proof.r_u.is_zero()) {
154            return Err(Error::BlindingNotZero);
155        }
156
157        let e = challenge;
158
159        // check that: R + cm⋅e == h⋅r_u + <g, u>
160        let lhs = proof.R + cm.mul(e);
161        // use msm_unchecked because we already ensured at the if that lengths match
162        let mut rhs = C::msm_unchecked(&params.generators[..proof.u.len()], &proof.u);
163        if H {
164            rhs += params.h.mul(proof.r_u);
165        }
166        if lhs != rhs {
167            return Err(Error::CommitmentVerificationFail);
168        }
169        Ok(())
170    }
171}
172
173pub type CF<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
174
175pub struct PedersenGadget<C, GC, const H: bool = false>
176where
177    C: CurveGroup,
178    GC: CurveVar<C, CF<C>>,
179{
180    _cf: PhantomData<CF<C>>,
181    _c: PhantomData<C>,
182    _gc: PhantomData<GC>,
183}
184
185use ark_r1cs_std::ToBitsGadget;
186impl<C, GC, const H: bool> PedersenGadget<C, GC, H>
187where
188    C: CurveGroup,
189    GC: CurveVar<C, CF<C>>,
190
191    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
192    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
193{
194    pub fn commit(
195        h: GC,
196        g: Vec<GC>,
197        v: Vec<Vec<Boolean<CF<C>>>>,
198        r: Vec<Boolean<CF<C>>>,
199    ) -> Result<GC, SynthesisError> {
200        let mut res = GC::zero();
201        if H {
202            res += h.scalar_mul_le(r.iter())?;
203        }
204        for (i, v_i) in v.iter().enumerate() {
205            res += g[i].scalar_mul_le(v_i.to_bits_le()?.iter())?;
206        }
207        Ok(res)
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use ark_ff::{BigInteger, PrimeField};
214    use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
215    use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
216    use ark_relations::r1cs::ConstraintSystem;
217    use ark_std::UniformRand;
218
219    use super::*;
220    use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};
221
222    #[test]
223    fn test_pedersen() {
224        test_pedersen_opt::<false>();
225        test_pedersen_opt::<true>();
226    }
227    fn test_pedersen_opt<const hiding: bool>() {
228        let mut rng = ark_std::test_rng();
229
230        let n: usize = 10;
231        // setup params
232        let (params, _) = Pedersen::<Projective>::setup(&mut rng, n).unwrap();
233        let poseidon_config = poseidon_canonical_config::<Fr>();
234
235        // init Prover's transcript
236        let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
237        // init Verifier's transcript
238        let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
239
240        let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
241            .take(n)
242            .collect();
243        // blinding factor
244        let r: Fr = if hiding {
245            Fr::rand(&mut rng)
246        } else {
247            Fr::zero()
248        };
249        let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap();
250        let proof =
251            Pedersen::<Projective, hiding>::prove(&params, &mut transcript_p, &cm, &v, &r, None)
252                .unwrap();
253        Pedersen::<Projective, hiding>::verify(&params, &mut transcript_v, &cm, &proof).unwrap();
254    }
255
256    #[test]
257    fn test_pedersen_circuit() {
258        test_pedersen_circuit_opt::<false>();
259        test_pedersen_circuit_opt::<true>();
260    }
261    fn test_pedersen_circuit_opt<const hiding: bool>() {
262        let mut rng = ark_std::test_rng();
263
264        let n: usize = 8;
265        // setup params
266        let (params, _) = Pedersen::<Projective, hiding>::setup(&mut rng, n).unwrap();
267
268        let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
269            .take(n)
270            .collect();
271        // blinding factor
272        let r: Fr = if hiding {
273            Fr::rand(&mut rng)
274        } else {
275            Fr::zero()
276        };
277        let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap();
278
279        let v_bits: Vec<Vec<bool>> = v.iter().map(|val| val.into_bigint().to_bits_le()).collect();
280        let r_bits: Vec<bool> = r.into_bigint().to_bits_le();
281
282        // circuit
283        let cs = ConstraintSystem::<Fq>::new_ref();
284
285        // prepare inputs
286        let vVar: Vec<Vec<Boolean<Fq>>> = v_bits
287            .iter()
288            .map(|val_bits| {
289                Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(val_bits.clone())).unwrap()
290            })
291            .collect();
292        let rVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
293        let gVar = Vec::<GVar>::new_witness(cs.clone(), || Ok(params.generators)).unwrap();
294        let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap();
295        let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap();
296
297        // use the gadget
298        let cmVar =
299            PedersenGadget::<Projective, GVar, hiding>::commit(hVar, gVar, vVar, rVar).unwrap();
300        cmVar.enforce_equal(&expected_cmVar).unwrap();
301    }
302}