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