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#[derive(Derivative)]
 63#[derivative(
 64    Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
 65    Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
 66)]
 67pub struct SignatureVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
 68where
 69    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 70{
 71    s: Vec<UInt8<ConstraintF<C>>>,
 72    r: GC,
 73    _curve: PhantomData<C>,
 74}
 75
 76impl<C, GC> AllocVar<Signature<C>, ConstraintF<C>> for SignatureVar<C, GC>
 77where
 78    C: ProjectiveCurve,
 79    GC: CurveVar<C, ConstraintF<C>>,
 80    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 81{
 82    fn new_variable<T: Borrow<Signature<C>>>(
 83        cs: impl Into<Namespace<ConstraintF<C>>>,
 84        f: impl FnOnce() -> Result<T, SynthesisError>,
 85        mode: AllocationMode,
 86    ) -> Result<Self, SynthesisError> {
 87        f().and_then(|val| {
 88            let cs = cs.into();
 89            // let s = val.borrow().s;
 90            let mut s = Vec::<UInt8<ConstraintF<C>>>::new();
 91            let s_bytes = to_bytes![val.borrow().s].unwrap();
 92            for i in 0..s_bytes.len() {
 93                s.push(UInt8::<ConstraintF<C>>::new_variable(
 94                    cs.clone(),
 95                    || Ok(s_bytes[i]),
 96                    mode,
 97                )?);
 98            }
 99
100            let r = GC::new_variable(cs, || Ok(val.borrow().r), mode)?;
101
102            Ok(Self {
103                s,
104                r,
105                _curve: PhantomData,
106            })
107        })
108    }
109}
110
111#[derive(Clone)]
112pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
113where
114    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
115{
116    generator: GC,
117    _curve: PhantomData<C>,
118}
119
120impl<C, GC> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GC>
121where
122    C: ProjectiveCurve,
123    GC: CurveVar<C, ConstraintF<C>>,
124    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
125{
126    fn new_variable<T: Borrow<Parameters<C>>>(
127        cs: impl Into<Namespace<ConstraintF<C>>>,
128        f: impl FnOnce() -> Result<T, SynthesisError>,
129        mode: AllocationMode,
130    ) -> Result<Self, SynthesisError> {
131        f().and_then(|val| {
132            let cs = cs.into();
133            let generator = GC::new_variable(cs, || Ok(val.borrow().generator), mode)?;
134            Ok(Self {
135                generator,
136                _curve: PhantomData,
137            })
138        })
139    }
140}
141
142pub struct BlindSigVerifyGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
143where
144    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
145{
146    _params: Parameters<C>, // TODO review if needed, maybe delete
147    _gc: PhantomData<GC>,
148}
149
150impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> BlindSigVerifyGadget<C, GC>
151where
152    C: ProjectiveCurve,
153    GC: CurveVar<C, ConstraintF<C>>,
154    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
155    ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
156        EdwardsParameters,
157        FpVar<Fp256<FqParameters>>,
158    >: From<GC>,
159    <C as ProjectiveCurve>::BaseField: PrimeField,
160    FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
161    FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
162{
163    fn verify(
164        parameters: &ParametersVar<C, GC>,
165        poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
166        m: FpVar<ConstraintF<C>>,
167        s: &SignatureVar<C, GC>,
168        q: &PublicKeyVar<C, GC>,
169    ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
170        let s_s = s.s.clone();
171
172        let sG = parameters
173            .generator
174            .scalar_mul_le(s_s.to_bits_le()?.iter())?;
175
176        // Note: in a circuit that aggregates multiple verifications, the hashing step could be
177        // done outside the signature verification, once for all 1 votes and once for all 0 votes,
178        // saving lots of constraints
179        let hm = poseidon_hash.hash(&[m])?;
180        let r = EdwardsVar::from(s.r.clone()); // WIP
181        let rx_fpvar: FpVar<ConstraintF<C>> = r.x.into();
182
183        // G * s == R + Q * (R.x * H(m))
184        let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?;
185        let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?;
186        let RHS = s.r.clone() + Q_rx_hm;
187
188        sG.is_eq(&RHS)
189    }
190}
191
192// example of circuit using BlindSigVerifyGadget
193#[derive(Clone)]
194pub struct BlindSigVerifyCircuit<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
195where
196    <C as ProjectiveCurve>::BaseField: PrimeField,
197{
198    _group: PhantomData<*const GC>,
199    pub params: Parameters<C>,
200    pub poseidon_hash_native: poseidon_native::Poseidon<ConstraintF<C>>,
201    pub signature: Option<Signature<C>>,
202    pub pub_key: Option<PublicKey<C>>,
203    pub message: Option<ConstraintF<C>>,
204}
205
206impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> ConstraintSynthesizer<ConstraintF<C>>
207    for BlindSigVerifyCircuit<C, GC>
208where
209    C: ProjectiveCurve,
210    GC: CurveVar<C, ConstraintF<C>>,
211    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
212    ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
213        EdwardsParameters,
214        FpVar<Fp256<FqParameters>>,
215    >: From<GC>,
216    <C as ProjectiveCurve>::BaseField: PrimeField,
217    FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
218    FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
219{
220    #[tracing::instrument(target = "r1cs", skip(self, cs))]
221    fn generate_constraints(
222        self,
223        cs: ConstraintSystemRef<ConstraintF<C>>,
224    ) -> Result<(), SynthesisError> {
225        let parameters =
226            ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), &self.params)?;
227
228        let pub_key =
229            PublicKeyVar::<C, GC>::new_input(ark_relations::ns!(cs, "public key"), || {
230                self.pub_key.ok_or(SynthesisError::AssignmentMissing)
231            })?;
232        let m = FpVar::<ConstraintF<C>>::new_input(ark_relations::ns!(cs, "message"), || {
233            self.message.ok_or(SynthesisError::AssignmentMissing)
234        })?;
235        let signature =
236            SignatureVar::<C, GC>::new_witness(ark_relations::ns!(cs, "signature"), || {
237                self.signature.ok_or(SynthesisError::AssignmentMissing)
238            })?;
239        let poseidon_hash = PoseidonGadget::<ConstraintF<C>>::from_native(
240            &mut cs.clone(),
241            self.poseidon_hash_native,
242        )
243        .unwrap();
244
245        let v = BlindSigVerifyGadget::<C, GC>::verify(
246            &parameters,
247            &poseidon_hash,
248            m,
249            &signature,
250            &pub_key,
251        )?;
252        v.enforce_equal(&Boolean::TRUE)
253    }
254}
255
256#[cfg(test)]
257mod test {
258    use super::*;
259    use crate::{poseidon_setup_params, BlindSigScheme};
260    use ark_ed_on_bn254::constraints::EdwardsVar as BabyJubJubVar;
261    use ark_ed_on_bn254::EdwardsProjective as BabyJubJub;
262
263    use arkworks_native_gadgets::poseidon;
264    use arkworks_utils::Curve;
265
266    use ark_relations::r1cs::ConstraintSystem;
267
268    type Fq = <BabyJubJub as ProjectiveCurve>::BaseField;
269    // type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
270    type S = BlindSigScheme<BabyJubJub>;
271
272    fn generate_native_data(
273        poseidon_hash: &poseidon::Poseidon<Fq>,
274    ) -> (
275        Parameters<BabyJubJub>,
276        PublicKey<BabyJubJub>,
277        Fq,
278        Signature<BabyJubJub>,
279    ) {
280        let mut rng = ark_std::test_rng();
281        let params = S::setup();
282        let (pk, sk) = S::keygen(&params, &mut rng);
283        let (k, signer_r) = S::new_request_params(&params, &mut rng);
284        let m = Fq::from(1234);
285        let (m_blinded, u) = S::blind(&params, &mut rng, &poseidon_hash, m, signer_r).unwrap();
286        let s_blinded = S::blind_sign(sk, k, m_blinded);
287        let s = S::unblind(s_blinded, u);
288        let verified = S::verify(&params, &poseidon_hash, m, s.clone(), pk);
289        assert!(verified);
290        (params, pk, m, s)
291    }
292
293    #[test]
294    fn test_verify() {
295        let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
296        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
297
298        // create signature using native-rust lib
299        let (params, pk, m, s) = generate_native_data(&poseidon_hash);
300
301        // use the constraint system to verify the signature
302        type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
303        let cs = ConstraintSystem::<Fq>::new_ref();
304
305        let params_var =
306            ParametersVar::<BabyJubJub, BabyJubJubVar>::new_constant(cs.clone(), params).unwrap();
307        let signature_var =
308            SignatureVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&s)).unwrap();
309        let pk_var =
310            PublicKeyVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&pk)).unwrap();
311        let m_var = FpVar::<Fq>::new_witness(cs.clone(), || Ok(&m)).unwrap();
312        let poseidon_hash_var =
313            PoseidonGadget::<Fq>::from_native(&mut cs.clone(), poseidon_hash).unwrap();
314
315        let valid_sig = SG::verify(
316            &params_var,
317            &poseidon_hash_var,
318            m_var,
319            &signature_var,
320            &pk_var,
321        )
322        .unwrap();
323        valid_sig.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
324        assert!(cs.is_satisfied().unwrap());
325    }
326
327    #[test]
328    fn test_constraint_system() {
329        let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
330        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
331
332        // create signature using native-rust lib
333        let (params, pk, m, s) = generate_native_data(&poseidon_hash);
334
335        // use the constraint system to verify the signature
336        let circuit = BlindSigVerifyCircuit::<BabyJubJub, BabyJubJubVar> {
337            params,
338            poseidon_hash_native: poseidon_hash,
339            signature: Some(s),
340            pub_key: Some(pk),
341            message: Some(m),
342            _group: PhantomData,
343        };
344        let cs = ConstraintSystem::<Fq>::new_ref();
345        circuit.generate_constraints(cs.clone()).unwrap();
346        let is_satisfied = cs.is_satisfied().unwrap();
347        assert!(is_satisfied);
348        println!("num_cnstraints={:?}", cs.num_constraints());
349    }
350}