arnaucube
arnaucube/sigmabus-pocPublic Self-hosted repository
sigmabus-poc/src/circuits.rs
src/circuits.rsRaw
  1use ark_crypto_primitives::crh::{
  2    poseidon::constraints::{CRHGadget, CRHParametersVar},
  3    CRHSchemeGadget,
  4};
  5use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
  6use ark_ec::{AffineRepr, CurveGroup};
  7use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar};
  8use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
  9use core::marker::PhantomData;
 10
 11// CF (ConstraintField)
 12pub type CF<C> = <<C as CurveGroup>::Affine as AffineRepr>::ScalarField;
 13
 14#[derive(Debug, Clone)]
 15pub struct GenZKCircuit<C: CurveGroup> {
 16    pub _c: PhantomData<C>,
 17    pub poseidon_config: PoseidonConfig<C::ScalarField>,
 18    // public
 19    pub cm: C::ScalarField,
 20    pub s: C::ScalarField,
 21    pub r_h: C::ScalarField,
 22    pub c: C::ScalarField,
 23    // private
 24    pub x: C::ScalarField,
 25    pub r: C::ScalarField,
 26    pub o_h: C::ScalarField,
 27}
 28impl<C: CurveGroup> ConstraintSynthesizer<CF<C>> for GenZKCircuit<C>
 29where
 30    C::ScalarField: Absorb,
 31{
 32    fn generate_constraints(self, cs: ConstraintSystemRef<CF<C>>) -> Result<(), SynthesisError> {
 33        // public inputs
 34        let cmVar = FpVar::<C::ScalarField>::new_input(cs.clone(), || Ok(self.cm))?;
 35        let sVar = FpVar::<C::ScalarField>::new_input(cs.clone(), || Ok(self.s))?;
 36        let r_hVar = FpVar::<C::ScalarField>::new_input(cs.clone(), || Ok(self.r_h))?;
 37        let cVar = FpVar::<C::ScalarField>::new_input(cs.clone(), || Ok(self.c))?;
 38
 39        // private inputs
 40        let xVar = FpVar::<C::ScalarField>::new_witness(cs.clone(), || Ok(self.x))?;
 41        let rVar = FpVar::<C::ScalarField>::new_witness(cs.clone(), || Ok(self.r))?;
 42        let o_hVar = FpVar::<C::ScalarField>::new_witness(cs.clone(), || Ok(self.o_h))?;
 43
 44        let crh_params =
 45            CRHParametersVar::<C::ScalarField>::new_witness(
 46                cs.clone(),
 47                || Ok(self.poseidon_config),
 48            )
 49            .unwrap();
 50
 51        Self::check(&crh_params, cmVar, sVar, r_hVar, cVar, xVar, rVar, o_hVar)?;
 52
 53        Ok(())
 54    }
 55}
 56
 57impl<C: CurveGroup> GenZKCircuit<C>
 58where
 59    C::ScalarField: Absorb,
 60{
 61    #[allow(clippy::too_many_arguments)]
 62    pub fn check(
 63        crh_params: &CRHParametersVar<C::ScalarField>,
 64        // public inputs:
 65        cm: FpVar<C::ScalarField>,
 66        s: FpVar<C::ScalarField>,
 67        r_h: FpVar<C::ScalarField>,
 68        c: FpVar<C::ScalarField>,
 69        // private inputs:
 70        x: FpVar<C::ScalarField>,
 71        r: FpVar<C::ScalarField>,
 72        o_h: FpVar<C::ScalarField>,
 73    ) -> Result<(), SynthesisError> {
 74        // cm == Commit(x) (Poseidon)
 75        let computed_cm = CRHGadget::<C::ScalarField>::evaluate(crh_params, &[x.clone()]).unwrap();
 76        computed_cm.enforce_equal(&cm)?;
 77
 78        // r_h == HCommit(r, o_h) (Poseidon)
 79        let computed_r_h =
 80            CRHGadget::<C::ScalarField>::evaluate(crh_params, &[r.clone(), o_h.clone()]).unwrap();
 81        computed_r_h.enforce_equal(&r_h)?;
 82
 83        // s == r + c * x
 84        s.enforce_equal(&(r + (c * x)))?;
 85        Ok(())
 86    }
 87}
 88
 89// Note: since at the v0.4.0 of ark_curves the bn254 curve does not have the constraints
 90// implemented, for the following tests we use the pallas curve.
 91#[cfg(test)]
 92pub mod tests {
 93    use super::*;
 94    use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
 95    use ark_ec::Group;
 96    use ark_ff::{BigInteger, PrimeField};
 97    use ark_r1cs_std::{
 98        alloc::AllocVar,
 99        boolean::Boolean,
100        fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
101        groups::GroupOpsBounds,
102        prelude::CurveVar,
103    };
104    use ark_relations::r1cs::ConstraintSystem;
105    use ark_std::UniformRand;
106    use std::ops::Mul;
107
108    // import pallas curve
109    use ark_pallas::{Fq, Fr, Projective};
110    // instead of ark_pallas::constraints::GVar we use a custom non-native version of it:
111    use ark_pallas::PallasConfig;
112    use ark_r1cs_std::groups::curves::short_weierstrass::ProjectiveVar;
113    pub type NonNativePallasGVar = ProjectiveVar<PallasConfig, NonNativeFieldVar<Fq, Fr>>;
114
115    use crate::sigmabus::SigmaProof;
116    use crate::transcript::{tests::poseidon_test_config, PoseidonTranscript};
117
118    #[test]
119    fn test_gen_zk() {
120        let mut rng = ark_std::test_rng();
121
122        let poseidon_config = poseidon_test_config::<Fr>();
123        let mut transcript = PoseidonTranscript::<Projective>::new(&poseidon_config);
124
125        let x = Fr::rand(&mut rng);
126
127        // the next lines do the same that is done in Sigmabus::prove but here in the test to have
128        // access to the internal values:
129        let mut sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
130        sponge.absorb(&x);
131        let cm: Fr = sponge.squeeze_field_elements(1)[0];
132        transcript.absorb(&cm);
133
134        let r = Fr::rand(&mut rng);
135        let o_h = Fr::rand(&mut rng);
136
137        let R = Projective::generator().mul(r);
138
139        let mut sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
140        sponge.absorb(&vec![r, o_h]);
141
142        let r_h: Fr = sponge.squeeze_field_elements(1)[0];
143
144        transcript.absorb_point(&R);
145        transcript.absorb(&r_h);
146        let c = transcript.get_challenge();
147
148        let s = r + c * x;
149        let sigma_proof = SigmaProof { s, R, r_h };
150        // end of Sigmabus::prove
151
152        let cs = ConstraintSystem::<Fr>::new_ref();
153
154        // public inputs
155        let cmVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(cm)).unwrap();
156        let sVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(sigma_proof.s)).unwrap();
157        let r_hVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(sigma_proof.r_h)).unwrap();
158        let cVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(c)).unwrap();
159        // private inputs
160        let xVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(x)).unwrap();
161        let rVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(r)).unwrap();
162        let o_hVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(o_h)).unwrap();
163
164        let crh_params =
165            CRHParametersVar::<Fr>::new_witness(cs.clone(), || Ok(poseidon_config)).unwrap();
166
167        // GenZK
168        GenZKCircuit::<Projective>::check(
169            &crh_params,
170            cmVar,
171            sVar,
172            r_hVar,
173            cVar,
174            xVar,
175            rVar,
176            o_hVar,
177        )
178        .unwrap();
179        assert!(cs.is_satisfied().unwrap());
180        dbg!(cs.num_constraints());
181    }
182
183    // This circuit implements the x*G operation that Sigmabus proves, but here we do it in the
184    // 'naive' way, which is computing it non-natively.
185    struct NonNativeScalarMulCircuit<
186        C: CurveGroup,
187        GC: CurveVar<C, C::ScalarField>,
188        FV: FieldVar<C::BaseField, C::ScalarField>,
189    > {
190        _gc: PhantomData<GC>,
191        _fv: PhantomData<FV>,
192        pub x: C::ScalarField,
193        pub X: C,
194    }
195    impl<C, GC, FV> ConstraintSynthesizer<CF<C>> for NonNativeScalarMulCircuit<C, GC, FV>
196    where
197        C: CurveGroup,
198        GC: CurveVar<C, C::ScalarField>,
199        FV: FieldVar<C::BaseField, C::ScalarField>,
200        for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
201    {
202        fn generate_constraints(
203            self,
204            cs: ConstraintSystemRef<CF<C>>,
205        ) -> Result<(), SynthesisError> {
206            let G = GC::new_constant(cs.clone(), C::generator())?;
207            let x_bits = Vec::<Boolean<CF<C>>>::new_input(cs.clone(), || {
208                Ok(self.x.into_bigint().to_bits_le())
209            })?;
210            let X = GC::new_input(cs.clone(), || Ok(self.X))?;
211
212            let xG = G.scalar_mul_le(x_bits.iter())?;
213            xG.enforce_equal(&X)?;
214
215            Ok(())
216        }
217    }
218
219    #[test]
220    fn test_nonnative_num_constraints() {
221        let mut rng = ark_std::test_rng();
222
223        // compute X = x * G
224        let x = Fr::rand(&mut rng);
225        let X = Projective::generator().mul(x);
226
227        let cs = ConstraintSystem::<Fr>::new_ref();
228        let nonnative_scalarmul_circuit = NonNativeScalarMulCircuit::<
229            Projective,
230            NonNativePallasGVar,
231            NonNativeFieldVar<Fq, Fr>,
232        > {
233            _gc: PhantomData,
234            _fv: PhantomData,
235            x,
236            X,
237        };
238
239        assert!(cs.is_satisfied().unwrap());
240        dbg!(cs.num_constraints());
241    }
242}