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