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(¶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 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(¶ms, &v, &r).unwrap();
254 let proof =
255 Pedersen::<Projective, hiding>::prove(¶ms, &mut transcript_p, &cm, &v, &r, None)
256 .unwrap();
257 Pedersen::<Projective, hiding>::verify(¶ms, &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(¶ms, &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}