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