arnaucube
arnaucube/ark-ec-blind-signaturesPublic Self-hosted repository
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            &parameters,
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            &parameters,
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(&params, &mut rng);
456        let (k, signer_r) = S::new_request_params(&params, &mut rng);
457        let m = [Fq::from(1234), Fq::from(5689), Fq::from(3456)];
458        let (m_blinded, u) = S::blind(&params, &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(&params, &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(&params, &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(&params, &mut rng);
482            let (m_blinded, u) = S::blind(&params, &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(&params, &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            &params_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}