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#[cfg(test)]
90pub mod tests {
91 use super::*;
92 use ark_bn254::{Fr, G1Projective};
93 use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
94 use ark_ec::Group;
95 use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar};
96 use ark_relations::r1cs::ConstraintSystem;
97 use ark_std::UniformRand;
98 use std::ops::Mul;
99
100 use crate::sigmabus::SigmaProof;
101 use crate::transcript::{tests::poseidon_test_config, PoseidonTranscript};
102
103 #[test]
104 fn test_gen_zk() {
105 let mut rng = ark_std::test_rng();
106
107 let poseidon_config = poseidon_test_config::<Fr>();
108 let mut transcript = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
109
110 let x = Fr::rand(&mut rng);
111
112 // the next lines do the same that is done in Sigmabus::prove but here in the test to have
113 // access to the internal values:
114 let mut sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
115 sponge.absorb(&x);
116 let cm: Fr = sponge.squeeze_field_elements(1)[0];
117 transcript.absorb(&cm);
118
119 let r = Fr::rand(&mut rng);
120 let o_h = Fr::rand(&mut rng);
121
122 let R = G1Projective::generator().mul(r);
123
124 let mut sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
125 sponge.absorb(&vec![r, o_h]);
126
127 let r_h: Fr = sponge.squeeze_field_elements(1)[0];
128
129 transcript.absorb_point(&R);
130 transcript.absorb(&r_h);
131 let c = transcript.get_challenge();
132
133 let s = r + c * x;
134 let sigma_proof = SigmaProof { s, R, r_h };
135 // end of Sigmabus::prove
136
137 let cs = ConstraintSystem::<Fr>::new_ref();
138
139 // public inputs
140 let cmVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(cm)).unwrap();
141 let sVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(sigma_proof.s)).unwrap();
142 let r_hVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(sigma_proof.r_h)).unwrap();
143 let cVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(c)).unwrap();
144 // private inputs
145 let xVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(x)).unwrap();
146 let rVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(r)).unwrap();
147 let o_hVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(o_h)).unwrap();
148
149 let crh_params =
150 CRHParametersVar::<Fr>::new_witness(cs.clone(), || Ok(poseidon_config)).unwrap();
151
152 // GenZK
153 GenZKCircuit::<G1Projective>::check(
154 &crh_params,
155 cmVar,
156 sVar,
157 r_hVar,
158 cVar,
159 xVar,
160 rVar,
161 o_hVar,
162 )
163 .unwrap();
164 assert!(cs.is_satisfied().unwrap());
165 dbg!("num_constraints={:?}", cs.num_constraints());
166 }
167}