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(¶ms.generators[..v.len()], v));
71 }
72 Ok(params.h.mul(r) + C::msm_unchecked(¶ms.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(¶ms.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(¶ms.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(¶ms, &v, &r).unwrap();
250 let proof =
251 Pedersen::<Projective, hiding>::prove(¶ms, &mut transcript_p, &cm, &v, &r, None)
252 .unwrap();
253 Pedersen::<Projective, hiding>::verify(¶ms, &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(¶ms, &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}