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 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            &parameters,
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            &parameters,
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(&params, &mut rng);
497        let (k, signer_r) = S::new_request_params(&params, &mut rng);
498        let m = [Fq::from(1234), Fq::from(5689), Fq::from(3456)];
499        let (m_blinded, u) = S::blind(&params, &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(&params, &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(&params, &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(&params, &mut rng);
523            let (m_blinded, u) = S::blind(&params, &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(&params, &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            &params_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}