arnaucube
arnaucube/ark-ec-blind-signaturesPublic Self-hosted repository
ark-ec-blind-signatures/src/schnorr_blind/constraints.rs
src/schnorr_blind/constraints.rsRaw
  1use crate::schnorr_blind::{ConstraintF, Msg, Parameters, PublicKey, Signature};
  2use crate::{constraints::BlindSigVerifyGadget, BlindSignatureScheme};
  3
  4use ark_ec::ProjectiveCurve;
  5use ark_ed_on_bn254::{constraints::EdwardsVar, EdwardsParameters, FqParameters};
  6use ark_ff::{fields::Fp256, to_bytes, PrimeField};
  7use ark_r1cs_std::{
  8    alloc::{AllocVar, AllocationMode},
  9    bits::uint8::UInt8,
 10    boolean::Boolean,
 11    eq::EqGadget,
 12    fields::fp::FpVar,
 13    groups::GroupOpsBounds,
 14    prelude::CurveVar,
 15    ToBitsGadget,
 16};
 17use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
 18use ark_std::ops::Mul;
 19
 20use core::{borrow::Borrow, marker::PhantomData};
 21use derivative::Derivative;
 22
 23// hash
 24use arkworks_native_gadgets::poseidon as poseidon_native;
 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(Derivative)]
 61#[derivative(
 62    Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
 63    Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
 64)]
 65pub struct MsgVar<const MSG_LEN: usize, C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
 66where
 67    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 68{
 69    m: [FpVar<ConstraintF<C>>; MSG_LEN],
 70    _gc: PhantomData<GC>,
 71}
 72impl<const MSG_LEN: usize, C, GC> MsgVar<MSG_LEN, C, GC>
 73where
 74    C: ProjectiveCurve,
 75    GC: CurveVar<C, ConstraintF<C>>,
 76    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 77{
 78    pub fn new(m: [FpVar<ConstraintF<C>>; MSG_LEN]) -> Self {
 79        Self {
 80            m,
 81            _gc: PhantomData,
 82        }
 83    }
 84}
 85
 86impl<const MSG_LEN: usize, C, GC> AllocVar<Msg<MSG_LEN, C>, ConstraintF<C>>
 87    for MsgVar<MSG_LEN, C, GC>
 88where
 89    C: ProjectiveCurve,
 90    GC: CurveVar<C, ConstraintF<C>>,
 91    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 92{
 93    fn new_variable<T: Borrow<Msg<MSG_LEN, C>>>(
 94        cs: impl Into<Namespace<ConstraintF<C>>>,
 95        f: impl FnOnce() -> Result<T, SynthesisError>,
 96        mode: AllocationMode,
 97    ) -> Result<Self, SynthesisError> {
 98        f().and_then(|m| {
 99            let m = m.borrow();
100            let cs = cs.into();
101            let msg_vec: Vec<FpVar<ConstraintF<C>>> =
102                Vec::new_variable(cs, || Ok(m.clone().0), mode)?;
103            let m: [FpVar<ConstraintF<C>>; MSG_LEN] =
104                msg_vec
105                    .try_into()
106                    .unwrap_or_else(|v: Vec<FpVar<ConstraintF<C>>>| {
107                        // WIP
108                        panic!(
109                            "Expected Vec of length: {}, actual length: {}",
110                            MSG_LEN,
111                            v.len()
112                        )
113                    });
114            Ok(Self {
115                m,
116                _gc: PhantomData,
117            })
118        })
119    }
120}
121
122#[derive(Derivative)]
123#[derivative(
124    Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
125    Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
126)]
127pub struct SignatureVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
128where
129    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
130{
131    s: Vec<UInt8<ConstraintF<C>>>,
132    r: GC,
133    _curve: PhantomData<C>,
134}
135
136impl<C, GC> AllocVar<Signature<C>, ConstraintF<C>> for SignatureVar<C, GC>
137where
138    C: ProjectiveCurve,
139    GC: CurveVar<C, ConstraintF<C>>,
140    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
141{
142    fn new_variable<T: Borrow<Signature<C>>>(
143        cs: impl Into<Namespace<ConstraintF<C>>>,
144        f: impl FnOnce() -> Result<T, SynthesisError>,
145        mode: AllocationMode,
146    ) -> Result<Self, SynthesisError> {
147        f().and_then(|val| {
148            let cs = cs.into();
149            // let s = val.borrow().s;
150            let mut s = Vec::<UInt8<ConstraintF<C>>>::new();
151            let s_bytes = to_bytes![val.borrow().s].unwrap();
152            #[allow(clippy::needless_range_loop)]
153            for i in 0..s_bytes.len() {
154                s.push(UInt8::<ConstraintF<C>>::new_variable(
155                    cs.clone(),
156                    || Ok(s_bytes[i]),
157                    mode,
158                )?);
159            }
160
161            let r = GC::new_variable(cs, || Ok(val.borrow().r), mode)?;
162
163            Ok(Self {
164                s,
165                r,
166                _curve: PhantomData,
167            })
168        })
169    }
170}
171
172#[derive(Clone)]
173pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
174where
175    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
176{
177    generator: GC,
178    _curve: PhantomData<C>,
179}
180
181impl<C, GC> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GC>
182where
183    C: ProjectiveCurve,
184    GC: CurveVar<C, ConstraintF<C>>,
185    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
186{
187    fn new_variable<T: Borrow<Parameters<C>>>(
188        cs: impl Into<Namespace<ConstraintF<C>>>,
189        f: impl FnOnce() -> Result<T, SynthesisError>,
190        mode: AllocationMode,
191    ) -> Result<Self, SynthesisError> {
192        f().and_then(|val| {
193            let cs = cs.into();
194            let generator = GC::new_variable(cs, || Ok(val.borrow().generator), mode)?;
195            Ok(Self {
196                generator,
197                _curve: PhantomData,
198            })
199        })
200    }
201}
202
203pub struct BlindSchnorrVerifyGadget<
204    const MSG_LEN: usize,
205    C: ProjectiveCurve,
206    GC: CurveVar<C, ConstraintF<C>>,
207> where
208    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
209{
210    _params: Parameters<C>, // TODO review if needed, maybe delete
211    _gc: PhantomData<GC>,
212}
213
214impl<
215        S: BlindSignatureScheme,
216        const MSG_LEN: usize,
217        C: ProjectiveCurve,
218        GC: CurveVar<C, ConstraintF<C>>,
219    > BlindSigVerifyGadget<S, ConstraintF<C>> for BlindSchnorrVerifyGadget<MSG_LEN, C, GC>
220where
221    C: ProjectiveCurve,
222    GC: CurveVar<C, ConstraintF<C>>,
223    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
224    ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
225        EdwardsParameters,
226        FpVar<Fp256<FqParameters>>,
227    >: From<GC>,
228    <C as ProjectiveCurve>::BaseField: PrimeField,
229    FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
230    FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
231
232    ParametersVar<C, GC>:
233        AllocVar<<S as BlindSignatureScheme>::Parameters, <C as ProjectiveCurve>::BaseField>,
234    PublicKeyVar<C, GC>:
235        AllocVar<<S as BlindSignatureScheme>::PublicKey, <C as ProjectiveCurve>::BaseField>,
236    SignatureVar<C, GC>:
237        AllocVar<<S as BlindSignatureScheme>::Signature, <C as ProjectiveCurve>::BaseField>,
238{
239    type ParametersVar = ParametersVar<C, GC>;
240    type PublicKeyVar = PublicKeyVar<C, GC>;
241    type SignatureVar = SignatureVar<C, GC>;
242    type Msg = Msg<MSG_LEN, C>;
243    type MsgVar = MsgVar<MSG_LEN, C, GC>;
244
245    fn verify(
246        parameters: &ParametersVar<C, GC>,
247        poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
248        m: &MsgVar<MSG_LEN, C, GC>,
249        s: &SignatureVar<C, GC>,
250        q: &PublicKeyVar<C, GC>,
251    ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
252        let sG = parameters
253            .generator
254            .scalar_mul_le(s.s.to_bits_le()?.iter())?;
255
256        // Note: in a circuit that aggregates multiple verifications, the hashing step could be
257        // done outside the signature verification, once for all 1 votes and once for all 0 votes,
258        // saving lots of constraints
259        let r = EdwardsVar::from(s.r.clone()); // WIP
260        let hm = poseidon_hash.hash(&m.m)?;
261        let to_hash = [r.x.into(), r.y.into(), hm];
262        let h = poseidon_hash.hash(&to_hash)?;
263
264        // G * s == R + H(R, m) * Q
265        let RHS = s.r.clone() + q.pub_key.scalar_mul_le(h.to_bits_le()?.iter())?;
266
267        sG.is_eq(&RHS)
268    }
269}
270
271// example of circuit using BlindSigVerifyGadget to verify a single blind signature
272#[derive(Clone)]
273pub struct BlindSigVerifyCircuit<
274    S: BlindSignatureScheme,
275    const MSG_LEN: usize,
276    C: ProjectiveCurve,
277    GC: CurveVar<C, ConstraintF<C>>,
278> where
279    <C as ProjectiveCurve>::BaseField: PrimeField,
280{
281    _s: PhantomData<S>,
282    _group: PhantomData<*const GC>,
283    pub params: Parameters<C>,
284    pub poseidon_hash_native: poseidon_native::Poseidon<ConstraintF<C>>,
285    pub signature: Option<Signature<C>>,
286    pub pub_key: Option<PublicKey<C>>,
287    pub message: Option<Msg<MSG_LEN, C>>,
288}
289
290impl<
291        S: BlindSignatureScheme,
292        const MSG_LEN: usize,
293        C: ProjectiveCurve,
294        GC: CurveVar<C, ConstraintF<C>>,
295    > ConstraintSynthesizer<ConstraintF<C>> for BlindSigVerifyCircuit<S, MSG_LEN, C, GC>
296where
297    C: ProjectiveCurve,
298    GC: CurveVar<C, ConstraintF<C>>,
299    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
300    ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
301        EdwardsParameters,
302        FpVar<Fp256<FqParameters>>,
303    >: From<GC>,
304    <C as ProjectiveCurve>::BaseField: PrimeField,
305    FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
306    FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
307
308    ParametersVar<C, GC>:
309        AllocVar<<S as BlindSignatureScheme>::Parameters, <C as ProjectiveCurve>::BaseField>,
310    PublicKeyVar<C, GC>:
311        AllocVar<<S as BlindSignatureScheme>::PublicKey, <C as ProjectiveCurve>::BaseField>,
312    SignatureVar<C, GC>:
313        AllocVar<<S as BlindSignatureScheme>::Signature, <C as ProjectiveCurve>::BaseField>,
314
315    Parameters<C>: Borrow<<S as BlindSignatureScheme>::Parameters>,
316    PublicKey<C>: Borrow<<S as BlindSignatureScheme>::PublicKey>,
317    Signature<C>: Borrow<<S as BlindSignatureScheme>::Signature>,
318{
319    #[tracing::instrument(target = "r1cs", skip(self, cs))]
320    fn generate_constraints(
321        self,
322        cs: ConstraintSystemRef<ConstraintF<C>>,
323    ) -> Result<(), SynthesisError> {
324        let parameters =
325            ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), self.params)?;
326
327        let pub_key =
328            PublicKeyVar::<C, GC>::new_input(ark_relations::ns!(cs, "public key"), || {
329                self.pub_key.ok_or(SynthesisError::AssignmentMissing)
330            })?;
331        let m = MsgVar::<MSG_LEN, C, GC>::new_input(ark_relations::ns!(cs, "message"), || {
332            self.message.ok_or(SynthesisError::AssignmentMissing)
333        })?;
334        let signature =
335            SignatureVar::<C, GC>::new_witness(ark_relations::ns!(cs, "signature"), || {
336                self.signature.ok_or(SynthesisError::AssignmentMissing)
337            })?;
338        #[allow(clippy::redundant_clone)]
339        let poseidon_hash = PoseidonGadget::<ConstraintF<C>>::from_native(
340            &mut cs.clone(),
341            self.poseidon_hash_native,
342        )
343        .unwrap();
344
345        let v = <BlindSchnorrVerifyGadget<MSG_LEN, C, GC> as BlindSigVerifyGadget<
346            S,
347            ConstraintF<C>,
348        >>::verify(&parameters, &poseidon_hash, &m, &signature, &pub_key)?;
349        v.enforce_equal(&Boolean::TRUE)
350    }
351}
352
353#[cfg(test)]
354mod test {
355    use super::*;
356    use crate::schnorr_blind::{poseidon_setup_params, SchnorrBlindSig};
357    use crate::BlindSignatureScheme;
358    use ark_ed_on_bn254::constraints::EdwardsVar as BabyJubJubVar;
359    use ark_ed_on_bn254::EdwardsProjective as BabyJubJub;
360
361    use arkworks_native_gadgets::poseidon;
362    use arkworks_utils::Curve;
363
364    use ark_relations::r1cs::ConstraintSystem;
365
366    type Fq = <BabyJubJub as ProjectiveCurve>::BaseField;
367    // type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
368    type S = SchnorrBlindSig<BabyJubJub>;
369
370    fn generate_single_sig_native_data(
371        poseidon_hash: &poseidon::Poseidon<Fq>,
372    ) -> (
373        Parameters<BabyJubJub>,
374        PublicKey<BabyJubJub>,
375        Msg<3, BabyJubJub>,
376        Signature<BabyJubJub>,
377    ) {
378        let mut rng = ark_std::test_rng();
379        let params = S::setup(poseidon_hash);
380        let (pk, sk) = S::keygen(&params, &mut rng);
381        let (k, signer_r) = S::new_request_params(&params, &mut rng);
382        let m = [Fq::from(1234), Fq::from(5689), Fq::from(3456)];
383        let (m_blinded, u) = S::blind(&params, &mut rng, &m, pk, signer_r).unwrap();
384        let s_blinded = S::blind_sign(sk, k, m_blinded);
385        let s = S::unblind(s_blinded, &u);
386        let verified = S::verify(&params, &m, s.clone(), pk);
387        assert!(verified);
388        (params, pk, Msg(m), s)
389    }
390
391    #[test]
392    fn test_single_verify() {
393        let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 4);
394        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
395        const MSG_LEN: usize = 3;
396
397        // create signature using native-rust lib
398        let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash);
399
400        // use the constraint system to verify the signature
401        let cs = ConstraintSystem::<Fq>::new_ref();
402
403        let params_var =
404            ParametersVar::<BabyJubJub, BabyJubJubVar>::new_constant(cs.clone(), params).unwrap();
405        let signature_var =
406            SignatureVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&s)).unwrap();
407        let pk_var =
408            PublicKeyVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&pk)).unwrap();
409        let m_var = MsgVar::<MSG_LEN, BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(m))
410            .unwrap();
411        let poseidon_hash_var =
412            PoseidonGadget::<Fq>::from_native(&mut cs.clone(), poseidon_hash).unwrap();
413
414        let valid_sig =
415            <BlindSchnorrVerifyGadget<MSG_LEN, BabyJubJub, BabyJubJubVar> as BlindSigVerifyGadget<
416                S,
417                ConstraintF<BabyJubJub>,
418            >>::verify(
419                &params_var,
420                &poseidon_hash_var,
421                &m_var,
422                &signature_var,
423                &pk_var,
424            )
425            .unwrap();
426        valid_sig.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
427        assert!(cs.is_satisfied().unwrap());
428    }
429
430    #[test]
431    fn test_single_verify_constraint_system() {
432        let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 4);
433        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
434        const MSG_LEN: usize = 3;
435
436        // create signature using native-rust lib
437        let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash);
438
439        // use the constraint system to verify the signature
440        let circuit = BlindSigVerifyCircuit::<S, MSG_LEN, BabyJubJub, BabyJubJubVar> {
441            params,
442            poseidon_hash_native: poseidon_hash.clone(),
443            signature: Some(s),
444            pub_key: Some(pk),
445            message: Some(m),
446            _group: PhantomData,
447            _s: PhantomData,
448        };
449        let cs = ConstraintSystem::<Fq>::new_ref();
450        circuit.generate_constraints(cs.clone()).unwrap();
451        let is_satisfied = cs.is_satisfied().unwrap();
452        assert!(is_satisfied);
453        println!("num_constraints={:?}", cs.num_constraints());
454    }
455}