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, 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, CanonicalSerialize, CanonicalDeserialize)]
 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::ScalarField>,
 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_nonnative(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_nonnative(&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::ScalarField>,
137        cm: &C,
138        proof: &Proof<C>,
139    ) -> Result<(), Error> {
140        transcript.absorb_nonnative(cm);
141        transcript.get_challenge(); // r_1
142        transcript.get_challenges(proof.u.len()); // d
143        transcript.absorb_nonnative(&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    pub fn commit(
199        h: &GC,
200        g: &[GC],
201        v: &[Vec<Boolean<CF<C>>>],
202        r: &[Boolean<CF<C>>],
203    ) -> Result<GC, SynthesisError> {
204        let mut res = GC::zero();
205        if H {
206            res += h.scalar_mul_le(r.iter())?;
207        }
208        for (i, v_i) in v.iter().enumerate() {
209            res += g[i].scalar_mul_le(v_i.to_bits_le()?.iter())?;
210        }
211        Ok(res)
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
218    use ark_ff::{BigInteger, PrimeField};
219    use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
220    use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
221    use ark_relations::r1cs::ConstraintSystem;
222
223    use super::*;
224    use crate::transcript::poseidon::poseidon_canonical_config;
225
226    #[test]
227    fn test_pedersen() {
228        test_pedersen_opt::<false>();
229        test_pedersen_opt::<true>();
230    }
231    fn test_pedersen_opt<const hiding: bool>() {
232        let mut rng = ark_std::test_rng();
233
234        let n: usize = 10;
235        // setup params
236        let (params, _) = Pedersen::<Projective>::setup(&mut rng, n).unwrap();
237        let poseidon_config = poseidon_canonical_config::<Fr>();
238
239        // init Prover's transcript
240        let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
241        // init Verifier's transcript
242        let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
243
244        let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
245            .take(n)
246            .collect();
247        // blinding factor
248        let r: Fr = if hiding {
249            Fr::rand(&mut rng)
250        } else {
251            Fr::zero()
252        };
253        let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap();
254        let proof =
255            Pedersen::<Projective, hiding>::prove(&params, &mut transcript_p, &cm, &v, &r, None)
256                .unwrap();
257        Pedersen::<Projective, hiding>::verify(&params, &mut transcript_v, &cm, &proof).unwrap();
258    }
259
260    #[test]
261    fn test_pedersen_circuit() {
262        test_pedersen_circuit_opt::<false>();
263        test_pedersen_circuit_opt::<true>();
264    }
265    fn test_pedersen_circuit_opt<const hiding: bool>() {
266        let mut rng = ark_std::test_rng();
267
268        let n: usize = 8;
269        // setup params
270        let (params, _) = Pedersen::<Projective, hiding>::setup(&mut rng, n).unwrap();
271
272        let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
273            .take(n)
274            .collect();
275        // blinding factor
276        let r: Fr = if hiding {
277            Fr::rand(&mut rng)
278        } else {
279            Fr::zero()
280        };
281        let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap();
282
283        let v_bits: Vec<Vec<bool>> = v.iter().map(|val| val.into_bigint().to_bits_le()).collect();
284        let r_bits: Vec<bool> = r.into_bigint().to_bits_le();
285
286        // circuit
287        let cs = ConstraintSystem::<Fq>::new_ref();
288
289        // prepare inputs
290        let vVar: Vec<Vec<Boolean<Fq>>> = v_bits
291            .iter()
292            .map(|val_bits| {
293                Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(val_bits.clone())).unwrap()
294            })
295            .collect();
296        let rVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
297        let gVar = Vec::<GVar>::new_witness(cs.clone(), || Ok(params.generators)).unwrap();
298        let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap();
299        let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap();
300
301        // use the gadget
302        let cmVar =
303            PedersenGadget::<Projective, GVar, hiding>::commit(&hVar, &gVar, &vVar, &rVar).unwrap();
304        cmVar.enforce_equal(&expected_cmVar).unwrap();
305    }
306}