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