ark-ec-blind-signatures/src/constraints.rs
src/constraints.rsRaw
1use crate::{Parameters, PublicKey, Signature};
2
3use ark_ec::ProjectiveCurve;
4use ark_ed_on_bn254::{constraints::EdwardsVar, EdwardsParameters, FqParameters};
5use ark_ff::{fields::Fp256, to_bytes, PrimeField};
6use ark_r1cs_std::{
7 alloc::{AllocVar, AllocationMode},
8 bits::uint8::UInt8,
9 boolean::Boolean,
10 eq::EqGadget,
11 fields::fp::FpVar,
12 groups::GroupOpsBounds,
13 prelude::CurveVar,
14 ToBitsGadget,
15};
16use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
17use ark_std::ops::Mul;
18
19use core::{borrow::Borrow, marker::PhantomData};
20use derivative::Derivative;
21
22// hash
23use arkworks_native_gadgets::poseidon as poseidon_native;
24// use arkworks_r1cs_gadgets::poseidon;
25use arkworks_r1cs_gadgets::poseidon::{FieldHasherGadget, PoseidonGadget};
26
27use crate::ConstraintF;
28
29#[derive(Derivative)]
30#[derivative(
31 Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
32 Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
33)]
34pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
35where
36 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
37{
38 pub_key: GC,
39 #[doc(hidden)]
40 _group: PhantomData<*const C>,
41}
42
43impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
44where
45 C: ProjectiveCurve,
46 GC: CurveVar<C, ConstraintF<C>>,
47 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
48{
49 fn new_variable<T: Borrow<PublicKey<C>>>(
50 cs: impl Into<Namespace<ConstraintF<C>>>,
51 f: impl FnOnce() -> Result<T, SynthesisError>,
52 mode: AllocationMode,
53 ) -> Result<Self, SynthesisError> {
54 let pub_key = GC::new_variable(cs, f, mode)?;
55 Ok(Self {
56 pub_key,
57 _group: PhantomData,
58 })
59 }
60}
61
62// TODO parametrize Msg & MsgVar length
63
64#[derive(Clone, Default, Debug)]
65// pub struct Msg<C: ProjectiveCurve>(pub Vec<ConstraintF<C>>);
66pub struct Msg<C: ProjectiveCurve>(pub [ConstraintF<C>; 3]);
67
68#[derive(Derivative)]
69#[derivative(
70 Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
71 Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
72)]
73pub struct MsgVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
74where
75 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
76{
77 m: [FpVar<ConstraintF<C>>; 3],
78 _gc: PhantomData<GC>,
79}
80impl<C, GC> AllocVar<Msg<C>, ConstraintF<C>> for MsgVar<C, GC>
81where
82 C: ProjectiveCurve,
83 GC: CurveVar<C, ConstraintF<C>>,
84 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
85{
86 fn new_variable<T: Borrow<Msg<C>>>(
87 cs: impl Into<Namespace<ConstraintF<C>>>,
88 f: impl FnOnce() -> Result<T, SynthesisError>,
89 mode: AllocationMode,
90 ) -> Result<Self, SynthesisError> {
91 f().and_then(|m| {
92 let m = m.borrow();
93 let cs = cs.into();
94 let msg: Vec<FpVar<ConstraintF<C>>> = Vec::new_variable(cs, || Ok(m.clone().0), mode)?;
95 Ok(Self {
96 m: [msg[0].clone(), msg[1].clone(), msg[2].clone()],
97 _gc: PhantomData,
98 })
99 })
100 }
101}
102
103#[derive(Derivative)]
104#[derivative(
105 Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
106 Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
107)]
108pub struct SignatureVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
109where
110 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
111{
112 s: Vec<UInt8<ConstraintF<C>>>,
113 r: GC,
114 _curve: PhantomData<C>,
115}
116
117impl<C, GC> AllocVar<Signature<C>, ConstraintF<C>> for SignatureVar<C, GC>
118where
119 C: ProjectiveCurve,
120 GC: CurveVar<C, ConstraintF<C>>,
121 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
122{
123 fn new_variable<T: Borrow<Signature<C>>>(
124 cs: impl Into<Namespace<ConstraintF<C>>>,
125 f: impl FnOnce() -> Result<T, SynthesisError>,
126 mode: AllocationMode,
127 ) -> Result<Self, SynthesisError> {
128 f().and_then(|val| {
129 let cs = cs.into();
130 // let s = val.borrow().s;
131 let mut s = Vec::<UInt8<ConstraintF<C>>>::new();
132 let s_bytes = to_bytes![val.borrow().s].unwrap();
133 #[allow(clippy::needless_range_loop)]
134 for i in 0..s_bytes.len() {
135 s.push(UInt8::<ConstraintF<C>>::new_variable(
136 cs.clone(),
137 || Ok(s_bytes[i]),
138 mode,
139 )?);
140 }
141
142 let r = GC::new_variable(cs, || Ok(val.borrow().r), mode)?;
143
144 Ok(Self {
145 s,
146 r,
147 _curve: PhantomData,
148 })
149 })
150 }
151}
152
153#[derive(Clone)]
154pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
155where
156 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
157{
158 generator: GC,
159 _curve: PhantomData<C>,
160}
161
162impl<C, GC> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GC>
163where
164 C: ProjectiveCurve,
165 GC: CurveVar<C, ConstraintF<C>>,
166 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
167{
168 fn new_variable<T: Borrow<Parameters<C>>>(
169 cs: impl Into<Namespace<ConstraintF<C>>>,
170 f: impl FnOnce() -> Result<T, SynthesisError>,
171 mode: AllocationMode,
172 ) -> Result<Self, SynthesisError> {
173 f().and_then(|val| {
174 let cs = cs.into();
175 let generator = GC::new_variable(cs, || Ok(val.borrow().generator), mode)?;
176 Ok(Self {
177 generator,
178 _curve: PhantomData,
179 })
180 })
181 }
182}
183
184pub struct BlindSigVerifyGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
185where
186 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
187{
188 _params: Parameters<C>, // TODO review if needed, maybe delete
189 _gc: PhantomData<GC>,
190}
191
192impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> BlindSigVerifyGadget<C, GC>
193where
194 C: ProjectiveCurve,
195 GC: CurveVar<C, ConstraintF<C>>,
196 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
197 ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
198 EdwardsParameters,
199 FpVar<Fp256<FqParameters>>,
200 >: From<GC>,
201 <C as ProjectiveCurve>::BaseField: PrimeField,
202 FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
203 FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
204{
205 fn verify(
206 parameters: &ParametersVar<C, GC>,
207 poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
208 m: &MsgVar<C, GC>,
209 s: &SignatureVar<C, GC>,
210 q: &PublicKeyVar<C, GC>,
211 ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
212 let sG = parameters
213 .generator
214 .scalar_mul_le(s.s.to_bits_le()?.iter())?;
215
216 // Note: in a circuit that aggregates multiple verifications, the hashing step could be
217 // done outside the signature verification, once for all 1 votes and once for all 0 votes,
218 // saving lots of constraints
219 let hm = poseidon_hash.hash(&m.m)?;
220 let r = EdwardsVar::from(s.r.clone()); // WIP
221 let rx_fpvar: FpVar<ConstraintF<C>> = r.x.into();
222
223 // G * s == R + Q * (R.x * H(m))
224 let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?;
225 let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?;
226 let RHS = s.r.clone() + Q_rx_hm;
227
228 sG.is_eq(&RHS)
229 }
230}
231
232pub struct BlindSigBatchVerifyGadget<
233 C: ProjectiveCurve,
234 GC: CurveVar<C, ConstraintF<C>>,
235 const NUM_SIGS: usize,
236> where
237 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
238{
239 _params: Parameters<C>, // TODO review if needed, maybe delete
240 _gc: PhantomData<GC>,
241}
242
243impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>, const NUM_SIGS: usize>
244 BlindSigBatchVerifyGadget<C, GC, NUM_SIGS>
245where
246 C: ProjectiveCurve,
247 GC: CurveVar<C, ConstraintF<C>>,
248 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
249 ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
250 EdwardsParameters,
251 FpVar<Fp256<FqParameters>>,
252 >: From<GC>,
253 <C as ProjectiveCurve>::BaseField: PrimeField,
254 FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
255 FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
256{
257 fn batch_verify(
258 parameters: &ParametersVar<C, GC>,
259 poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
260 m: &MsgVar<C, GC>,
261 sigs: &[SignatureVar<C, GC>],
262 q: &PublicKeyVar<C, GC>,
263 ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
264 // Note: in a circuit that aggregates multiple verifications, the hashing step could be
265 // done outside the signature verification, once for all 1 votes and once for all 0 votes,
266 // saving lots of constraints
267 let hm = poseidon_hash.hash(&m.m)?;
268
269 #[allow(clippy::needless_range_loop)]
270 for i in 0..NUM_SIGS {
271 let sG = parameters
272 .generator
273 .scalar_mul_le(sigs[i].s.to_bits_le()?.iter())?;
274
275 let r = EdwardsVar::from(sigs[i].r.clone()); // WIP
276 let rx_fpvar: FpVar<ConstraintF<C>> = r.x.into();
277
278 // G * s == R + Q * (R.x * H(m))
279 let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?;
280 let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?;
281 let RHS = sigs[i].r.clone() + Q_rx_hm;
282 sG.enforce_equal(&RHS)?;
283 }
284 Ok(Boolean::TRUE)
285 }
286}
287
288// example of circuit using BlindSigVerifyGadget to verify a single blind signature
289#[derive(Clone)]
290pub struct BlindSigVerifyCircuit<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
291where
292 <C as ProjectiveCurve>::BaseField: PrimeField,
293{
294 _group: PhantomData<*const GC>,
295 pub params: Parameters<C>,
296 pub poseidon_hash_native: poseidon_native::Poseidon<ConstraintF<C>>,
297 pub signature: Option<Signature<C>>,
298 pub pub_key: Option<PublicKey<C>>,
299 pub message: Option<Msg<C>>,
300}
301
302impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> ConstraintSynthesizer<ConstraintF<C>>
303 for BlindSigVerifyCircuit<C, GC>
304where
305 C: ProjectiveCurve,
306 GC: CurveVar<C, ConstraintF<C>>,
307 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
308 ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
309 EdwardsParameters,
310 FpVar<Fp256<FqParameters>>,
311 >: From<GC>,
312 <C as ProjectiveCurve>::BaseField: PrimeField,
313 FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
314 FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
315{
316 #[tracing::instrument(target = "r1cs", skip(self, cs))]
317 fn generate_constraints(
318 self,
319 cs: ConstraintSystemRef<ConstraintF<C>>,
320 ) -> Result<(), SynthesisError> {
321 let parameters =
322 ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), &self.params)?;
323
324 let pub_key =
325 PublicKeyVar::<C, GC>::new_input(ark_relations::ns!(cs, "public key"), || {
326 self.pub_key.ok_or(SynthesisError::AssignmentMissing)
327 })?;
328 let m = MsgVar::<C, GC>::new_input(ark_relations::ns!(cs, "message"), || {
329 self.message.ok_or(SynthesisError::AssignmentMissing)
330 })?;
331 let signature =
332 SignatureVar::<C, GC>::new_witness(ark_relations::ns!(cs, "signature"), || {
333 self.signature.ok_or(SynthesisError::AssignmentMissing)
334 })?;
335 #[allow(clippy::redundant_clone)]
336 let poseidon_hash = PoseidonGadget::<ConstraintF<C>>::from_native(
337 &mut cs.clone(),
338 self.poseidon_hash_native,
339 )
340 .unwrap();
341
342 let v = BlindSigVerifyGadget::<C, GC>::verify(
343 ¶meters,
344 &poseidon_hash,
345 &m,
346 &signature,
347 &pub_key,
348 )?;
349 v.enforce_equal(&Boolean::TRUE)
350 }
351}
352
353// example of circuit using BlindSigVerifyGadget to verify a batch of blind signatures
354#[derive(Clone)]
355pub struct BlindSigBatchVerifyCircuit<
356 C: ProjectiveCurve,
357 GC: CurveVar<C, ConstraintF<C>>,
358 const NUM_SIGS: usize,
359> where
360 <C as ProjectiveCurve>::BaseField: PrimeField,
361{
362 _group: PhantomData<*const GC>,
363 pub params: Parameters<C>,
364 pub poseidon_hash_native: poseidon_native::Poseidon<ConstraintF<C>>,
365 pub signatures: Option<Vec<Signature<C>>>,
366 pub pub_key: Option<PublicKey<C>>,
367 pub message: Option<Msg<C>>,
368}
369
370impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>, const NUM_SIGS: usize>
371 ConstraintSynthesizer<ConstraintF<C>> for BlindSigBatchVerifyCircuit<C, GC, NUM_SIGS>
372where
373 C: ProjectiveCurve,
374 GC: CurveVar<C, ConstraintF<C>>,
375 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
376 ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
377 EdwardsParameters,
378 FpVar<Fp256<FqParameters>>,
379 >: From<GC>,
380 <C as ProjectiveCurve>::BaseField: PrimeField,
381 FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
382 FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
383{
384 #[tracing::instrument(target = "r1cs", skip(self, cs))]
385 fn generate_constraints(
386 self,
387 cs: ConstraintSystemRef<ConstraintF<C>>,
388 ) -> Result<(), SynthesisError> {
389 let parameters =
390 ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), &self.params)?;
391
392 let pub_key =
393 PublicKeyVar::<C, GC>::new_input(ark_relations::ns!(cs, "public key"), || {
394 self.pub_key.ok_or(SynthesisError::AssignmentMissing)
395 })?;
396 let m = MsgVar::<C, GC>::new_input(ark_relations::ns!(cs, "message"), || {
397 self.message.ok_or(SynthesisError::AssignmentMissing)
398 })?;
399
400 let mut signatures: Vec<SignatureVar<C, GC>> = Vec::new();
401 for i in 0..NUM_SIGS {
402 let signature = self.signatures.as_ref().and_then(|s| s.get(i));
403
404 let signature =
405 SignatureVar::<C, GC>::new_witness(ark_relations::ns!(cs, "signature"), || {
406 signature.ok_or(SynthesisError::AssignmentMissing)
407 })?;
408 signatures.push(signature);
409 }
410
411 #[allow(clippy::redundant_clone)]
412 let poseidon_hash = PoseidonGadget::<ConstraintF<C>>::from_native(
413 &mut cs.clone(),
414 self.poseidon_hash_native,
415 )
416 .unwrap();
417
418 let v = BlindSigBatchVerifyGadget::<C, GC, NUM_SIGS>::batch_verify(
419 ¶meters,
420 &poseidon_hash,
421 &m,
422 &signatures,
423 &pub_key,
424 )?;
425 v.enforce_equal(&Boolean::TRUE)
426 }
427}
428
429#[cfg(test)]
430mod test {
431 use super::*;
432 use crate::{poseidon_setup_params, BlindSigScheme};
433 use ark_ed_on_bn254::constraints::EdwardsVar as BabyJubJubVar;
434 use ark_ed_on_bn254::EdwardsProjective as BabyJubJub;
435
436 use arkworks_native_gadgets::poseidon;
437 use arkworks_utils::Curve;
438
439 use ark_relations::r1cs::ConstraintSystem;
440
441 type Fq = <BabyJubJub as ProjectiveCurve>::BaseField;
442 // type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
443 type S = BlindSigScheme<BabyJubJub>;
444
445 fn generate_single_sig_native_data(
446 poseidon_hash: &poseidon::Poseidon<Fq>,
447 ) -> (
448 Parameters<BabyJubJub>,
449 PublicKey<BabyJubJub>,
450 Msg<BabyJubJub>,
451 Signature<BabyJubJub>,
452 ) {
453 let mut rng = ark_std::test_rng();
454 let params = S::setup();
455 let (pk, sk) = S::keygen(¶ms, &mut rng);
456 let (k, signer_r) = S::new_request_params(¶ms, &mut rng);
457 let m = [Fq::from(1234), Fq::from(5689), Fq::from(3456)];
458 let (m_blinded, u) = S::blind(¶ms, &mut rng, &poseidon_hash, &m, signer_r).unwrap();
459 let s_blinded = S::blind_sign(sk, k, m_blinded);
460 let s = S::unblind(s_blinded, u);
461 let verified = S::verify(¶ms, &poseidon_hash, &m, s.clone(), pk);
462 assert!(verified);
463 (params, pk, Msg(m), s)
464 }
465
466 fn generate_batch_sig_native_data(
467 poseidon_hash: &poseidon::Poseidon<Fq>,
468 n: usize,
469 ) -> (
470 Parameters<BabyJubJub>,
471 PublicKey<BabyJubJub>,
472 Msg<BabyJubJub>,
473 Vec<Signature<BabyJubJub>>,
474 ) {
475 let mut rng = ark_std::test_rng();
476 let params = S::setup();
477 let (pk, sk) = S::keygen(¶ms, &mut rng);
478 let m = [Fq::from(1234), Fq::from(5689), Fq::from(3456)];
479 let mut signatures: Vec<Signature<BabyJubJub>> = Vec::new();
480 for _ in 0..n {
481 let (k, signer_r) = S::new_request_params(¶ms, &mut rng);
482 let (m_blinded, u) = S::blind(¶ms, &mut rng, &poseidon_hash, &m, signer_r).unwrap();
483 let s_blinded = S::blind_sign(sk, k, m_blinded);
484 let s = S::unblind(s_blinded, u);
485 let verified = S::verify(¶ms, &poseidon_hash, &m, s.clone(), pk);
486 assert!(verified);
487 signatures.push(s);
488 }
489 (params, pk, Msg(m), signatures)
490 }
491
492 #[test]
493 fn test_single_verify() {
494 // TODO N_INPUTS+1 (N_INPUTS msg_to_sign_length)
495 let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 4);
496 let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
497
498 // create signature using native-rust lib
499 let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash);
500
501 // use the constraint system to verify the signature
502 type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
503 let cs = ConstraintSystem::<Fq>::new_ref();
504
505 let params_var =
506 ParametersVar::<BabyJubJub, BabyJubJubVar>::new_constant(cs.clone(), params).unwrap();
507 let signature_var =
508 SignatureVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&s)).unwrap();
509 let pk_var =
510 PublicKeyVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&pk)).unwrap();
511 let m_var = MsgVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(m)).unwrap();
512 let poseidon_hash_var =
513 PoseidonGadget::<Fq>::from_native(&mut cs.clone(), poseidon_hash).unwrap();
514
515 let valid_sig = SG::verify(
516 ¶ms_var,
517 &poseidon_hash_var,
518 &m_var,
519 &signature_var,
520 &pk_var,
521 )
522 .unwrap();
523 valid_sig.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
524 assert!(cs.is_satisfied().unwrap());
525 }
526
527 #[test]
528 fn test_single_verify_constraint_system() {
529 let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 4);
530 let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
531
532 // create signature using native-rust lib
533 let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash);
534
535 // use the constraint system to verify the signature
536 let circuit = BlindSigVerifyCircuit::<BabyJubJub, BabyJubJubVar> {
537 params,
538 poseidon_hash_native: poseidon_hash.clone(),
539 signature: Some(s),
540 pub_key: Some(pk),
541 message: Some(m),
542 _group: PhantomData,
543 };
544 let cs = ConstraintSystem::<Fq>::new_ref();
545 circuit.generate_constraints(cs.clone()).unwrap();
546 let is_satisfied = cs.is_satisfied().unwrap();
547 assert!(is_satisfied);
548 println!("num_cnstraints={:?}", cs.num_constraints());
549 }
550
551 #[test]
552 fn test_batch_verify_constraint_system() {
553 let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 4);
554 let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
555
556 // create signatures using native-rust lib
557 const NUM_SIGS: usize = 5;
558 let (params, pk, m, sigs) = generate_batch_sig_native_data(&poseidon_hash, NUM_SIGS);
559
560 // use the constraint system to verify the batch of signatures
561 let circuit = BlindSigBatchVerifyCircuit::<BabyJubJub, BabyJubJubVar, NUM_SIGS> {
562 params,
563 poseidon_hash_native: poseidon_hash.clone(),
564 signatures: Some(sigs),
565 pub_key: Some(pk),
566 message: Some(m),
567 _group: PhantomData,
568 };
569 let cs = ConstraintSystem::<Fq>::new_ref();
570 circuit.generate_constraints(cs.clone()).unwrap();
571 let is_satisfied = cs.is_satisfied().unwrap();
572 assert!(is_satisfied);
573 println!("num_cnstraints={:?}", cs.num_constraints());
574 }
575}