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