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, 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(¶ms.generators[..v.len()], v));
78 }
79 Ok(params.h.mul(r) + C::msm_unchecked(¶ms.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(¶ms.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(¶ms.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_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
221 use ark_ff::{BigInteger, PrimeField};
222 use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
223 use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
224 use ark_relations::r1cs::ConstraintSystem;
225
226 use super::*;
227 use crate::transcript::poseidon::poseidon_canonical_config;
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 = PoseidonSponge::<Fr>::new(&poseidon_config);
244 // init Verifier's transcript
245 let mut transcript_v = PoseidonSponge::<Fr>::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(¶ms, &v, &r).unwrap();
257 let proof =
258 Pedersen::<Projective, hiding>::prove(¶ms, &mut transcript_p, &cm, &v, &r, None)
259 .unwrap();
260 Pedersen::<Projective, hiding>::verify(¶ms, &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(¶ms, &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}