ark-ec-blind-signatures/src/constraints.rs
src/constraints.rsRaw
1use crate::{Parameters, PublicKey, Signature};
2
3use ark_ec::{AffineCurve, ProjectiveCurve};
4use ark_ed_on_bn254::{constraints::EdwardsVar, EdwardsParameters, FqParameters};
5use ark_ff::{
6 fields::{Field, Fp256},
7 to_bytes, PrimeField, ToConstraintField,
8};
9use ark_r1cs_std::{
10 alloc::{AllocVar, AllocationMode},
11 bits::uint8::UInt8,
12 boolean::Boolean,
13 eq::EqGadget,
14 fields::{fp::FpVar, FieldVar},
15 groups::{curves::twisted_edwards::AffineVar, GroupOpsBounds},
16 prelude::CurveVar,
17 ToBitsGadget,
18};
19use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
20use ark_std::ops::{Add, Mul};
21
22use core::{borrow::Borrow, marker::PhantomData};
23use derivative::Derivative;
24
25// hash
26use arkworks_native_gadgets::poseidon as poseidon_native;
27use arkworks_r1cs_gadgets::poseidon;
28use arkworks_r1cs_gadgets::poseidon::{FieldHasherGadget, PoseidonGadget, PoseidonParametersVar};
29
30use crate::ConstraintF;
31
32#[derive(Derivative)]
33#[derivative(
34 Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
35 Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
36)]
37pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
38where
39 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
40{
41 pub_key: GC,
42 #[doc(hidden)]
43 _group: PhantomData<*const C>,
44}
45
46impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
47where
48 C: ProjectiveCurve,
49 GC: CurveVar<C, ConstraintF<C>>,
50 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
51{
52 fn new_variable<T: Borrow<PublicKey<C>>>(
53 cs: impl Into<Namespace<ConstraintF<C>>>,
54 f: impl FnOnce() -> Result<T, SynthesisError>,
55 mode: AllocationMode,
56 ) -> Result<Self, SynthesisError> {
57 let pub_key = GC::new_variable(cs, f, mode)?;
58 Ok(Self {
59 pub_key,
60 _group: PhantomData,
61 })
62 }
63}
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 SignatureVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
71where
72 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
73{
74 s: Vec<UInt8<ConstraintF<C>>>,
75 r: GC,
76 _curve: PhantomData<C>,
77}
78
79impl<C, GC> AllocVar<Signature<C>, ConstraintF<C>> for SignatureVar<C, GC>
80where
81 C: ProjectiveCurve,
82 GC: CurveVar<C, ConstraintF<C>>,
83 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
84{
85 fn new_variable<T: Borrow<Signature<C>>>(
86 cs: impl Into<Namespace<ConstraintF<C>>>,
87 f: impl FnOnce() -> Result<T, SynthesisError>,
88 mode: AllocationMode,
89 ) -> Result<Self, SynthesisError> {
90 f().and_then(|val| {
91 let cs = cs.into();
92 // let s = val.borrow().s;
93 let mut s = Vec::<UInt8<ConstraintF<C>>>::new();
94 let s_bytes = to_bytes![val.borrow().s].unwrap();
95 for i in 0..s_bytes.len() {
96 s.push(UInt8::<ConstraintF<C>>::new_variable(
97 cs.clone(),
98 || Ok(s_bytes[i].clone()),
99 mode,
100 )?);
101 }
102
103 let r = GC::new_variable(cs.clone(), || Ok(val.borrow().r), mode)?;
104
105 Ok(Self {
106 s,
107 r,
108 _curve: PhantomData,
109 })
110 })
111 }
112}
113
114#[derive(Clone)]
115pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
116where
117 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
118{
119 generator: GC,
120 _curve: PhantomData<C>,
121}
122
123impl<C, GC> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GC>
124where
125 C: ProjectiveCurve,
126 GC: CurveVar<C, ConstraintF<C>>,
127 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
128{
129 fn new_variable<T: Borrow<Parameters<C>>>(
130 cs: impl Into<Namespace<ConstraintF<C>>>,
131 f: impl FnOnce() -> Result<T, SynthesisError>,
132 mode: AllocationMode,
133 ) -> Result<Self, SynthesisError> {
134 f().and_then(|val| {
135 let cs = cs.into();
136 let generator = GC::new_variable(cs.clone(), || Ok(val.borrow().generator), mode)?;
137 Ok(Self {
138 generator,
139 _curve: PhantomData,
140 })
141 })
142 }
143}
144
145pub struct BlindSigVerifyGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
146where
147 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
148{
149 _params: Parameters<C>, // TODO review if needed, maybe delete
150 _gc: PhantomData<GC>,
151}
152
153impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> BlindSigVerifyGadget<C, GC>
154where
155 C: ProjectiveCurve,
156 GC: CurveVar<C, ConstraintF<C>>,
157 for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
158 ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
159 EdwardsParameters,
160 FpVar<Fp256<FqParameters>>,
161 >: From<GC>,
162 <C as ProjectiveCurve>::BaseField: PrimeField,
163 FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
164 FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
165{
166 fn verify(
167 parameters: &ParametersVar<C, GC>,
168 poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
169 m: FpVar<ConstraintF<C>>,
170 s: &SignatureVar<C, GC>,
171 q: &PublicKeyVar<C, GC>,
172 ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
173 let s_s = s.s.clone();
174
175 let sG = parameters
176 .generator
177 .scalar_mul_le(s_s.to_bits_le()?.iter())?;
178
179 // Note: in a circuit that aggregates multiple verifications, the hashing step could be
180 // done outside the signature verification, once for all 1 votes and once for all 0 votes,
181 // saving lots of constraints
182 let hm = poseidon_hash.hash(&[m])?;
183 let r = EdwardsVar::from(s.r.clone()); // WIP
184 let rx_fpvar: FpVar<ConstraintF<C>> = r.x.into();
185
186 // G * s == R + Q * (R.x * H(m))
187 let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?;
188 let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?;
189 let RHS = s.r.clone() + Q_rx_hm;
190
191 sG.is_eq(&RHS)
192 }
193}
194
195#[cfg(test)]
196mod test {
197 use super::*;
198 use crate::{poseidon_setup_params, BlindSigScheme};
199 use ark_ed_on_bn254::constraints::EdwardsVar as BabyJubJubVar;
200 use ark_ed_on_bn254::EdwardsProjective as BabyJubJub;
201
202 use arkworks_native_gadgets::poseidon;
203 use arkworks_utils::Curve;
204
205 use ark_relations::r1cs::ConstraintSystem;
206
207 type Fq = <BabyJubJub as ProjectiveCurve>::BaseField;
208 // type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
209 type S = BlindSigScheme<BabyJubJub>;
210
211 #[test]
212 fn test_verify() {
213 let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
214 let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
215 let mut rng = ark_std::test_rng();
216
217 // create signature using native-rust lib
218 let params = S::setup();
219 let (pk, sk) = S::keygen(¶ms, &mut rng);
220 let (k, signer_r) = S::new_request_params(¶ms, &mut rng);
221 let m = Fq::from(1234);
222 let (m_blinded, u) = S::blind(¶ms, &mut rng, &poseidon_hash, m, signer_r).unwrap();
223 let s_blinded = S::blind_sign(sk, k, m_blinded);
224 let s = S::unblind(s_blinded, u);
225 let verified = S::verify(¶ms, &poseidon_hash, m, s.clone(), pk);
226 assert!(verified);
227
228 // use the constraint system to verify the signature
229 type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
230 let cs = ConstraintSystem::<Fq>::new_ref();
231
232 let params_var =
233 ParametersVar::<BabyJubJub, BabyJubJubVar>::new_constant(cs.clone(), params).unwrap();
234 let signature_var =
235 SignatureVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&s)).unwrap();
236 let pk_var =
237 PublicKeyVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&pk)).unwrap();
238 let m_var = FpVar::<Fq>::new_witness(cs.clone(), || Ok(&m)).unwrap();
239 let poseidon_hash_var =
240 PoseidonGadget::<Fq>::from_native(&mut cs.clone(), poseidon_hash).unwrap();
241
242 let valid_sig = SG::verify(
243 ¶ms_var,
244 &poseidon_hash_var,
245 m_var,
246 &signature_var,
247 &pk_var,
248 )
249 .unwrap();
250 valid_sig.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
251 assert!(cs.is_satisfied().unwrap());
252 }
253}