1use ff::PrimeField;
23use midnight_proofs::{circuit::Layouter, plonk::Error};
24
25use crate::{
26 instructions::{AssertionInstructions, EqualityInstructions},
27 types::{AssignedBit, InnerConstants},
28};
29
30pub trait ZeroInstructions<F, Assigned>:
32 AssertionInstructions<F, Assigned> + EqualityInstructions<F, Assigned>
33where
34 F: PrimeField,
35 Assigned: InnerConstants,
36{
37 fn assert_zero(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<(), Error> {
48 self.assert_equal_to_fixed(layouter, x, Assigned::inner_zero())
49 }
50
51 fn assert_non_zero(&self, layouter: &mut impl Layouter<F>, x: &Assigned) -> Result<(), Error> {
53 self.assert_not_equal_to_fixed(layouter, x, Assigned::inner_zero())
54 }
55
56 fn is_zero(
67 &self,
68 layouter: &mut impl Layouter<F>,
69 x: &Assigned,
70 ) -> Result<AssignedBit<F>, Error> {
71 self.is_equal_to_fixed(layouter, x, Assigned::inner_zero())
72 }
73}
74
75#[cfg(test)]
76pub(crate) mod tests {
77 use std::marker::PhantomData;
78
79 use ff::FromUniformBytes;
80 use midnight_proofs::{
81 circuit::{Layouter, SimpleFloorPlanner},
82 dev::MockProver,
83 plonk::{Circuit, ConstraintSystem},
84 };
85 use rand::SeedableRng;
86 use rand_chacha::ChaCha8Rng;
87
88 use super::*;
89 use crate::{
90 instructions::AssignmentInstructions,
91 testing_utils::{FromScratch, Sampleable},
92 types::{AssignedNative, InnerValue},
93 utils::circuit_modeling::circuit_to_json,
94 };
95
96 #[derive(Clone, Debug)]
97 enum Operation {
98 Assert,
99 AssertNon,
100 IsZero,
101 }
102
103 #[derive(Clone, Debug)]
104 struct TestCircuit<F, Assigned, ZeroChip>
105 where
106 Assigned: InnerValue,
107 {
108 x: Assigned::Element,
109 expected: Option<bool>,
110 operation: Operation,
111 _marker: PhantomData<(F, Assigned, ZeroChip)>,
112 }
113
114 impl<F, Assigned, ZeroChip> Circuit<F> for TestCircuit<F, Assigned, ZeroChip>
115 where
116 F: PrimeField,
117 Assigned: InnerConstants,
118 ZeroChip:
119 ZeroInstructions<F, Assigned> + AssignmentInstructions<F, Assigned> + FromScratch<F>,
120 {
121 type Config = <ZeroChip as FromScratch<F>>::Config;
122 type FloorPlanner = SimpleFloorPlanner;
123 type Params = ();
124
125 fn without_witnesses(&self) -> Self {
126 unreachable!()
127 }
128
129 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
130 let committed_instance_column = meta.instance_column();
131 let instance_column = meta.instance_column();
132 let constants_column = meta.fixed_column();
133 meta.enable_constant(constants_column);
134 ZeroChip::configure_from_scratch(meta, &[committed_instance_column, instance_column])
135 }
136
137 fn synthesize(
138 &self,
139 config: Self::Config,
140 mut layouter: impl Layouter<F>,
141 ) -> Result<(), Error> {
142 let chip = ZeroChip::new_from_scratch(&config);
143
144 let x = chip.assign_fixed(&mut layouter, self.x.clone())?;
145 match self.operation {
146 Operation::Assert => chip.assert_zero(&mut layouter, &x),
147 Operation::AssertNon => chip.assert_non_zero(&mut layouter, &x),
148 Operation::IsZero => {
149 let res = chip.is_zero(&mut layouter, &x)?;
150 let res_as_value: AssignedNative<F> = res.into();
151 layouter.assign_region(
152 || "assert contains fixed",
153 |mut region| {
154 region.constrain_constant(
155 res_as_value.cell(),
156 if self.expected.unwrap() {
157 F::ONE
158 } else {
159 F::ZERO
160 },
161 )
162 },
163 )
164 }
165 }?;
166
167 chip.load_from_scratch(&mut layouter)
168 }
169 }
170
171 fn run<F, Assigned, ZeroChip>(
172 x: &Assigned::Element,
173 expected: Option<bool>,
174 operation: Operation,
175 must_pass: bool,
176 cost_model: bool,
177 chip_name: &str,
178 op_name: &str,
179 ) where
180 F: PrimeField + FromUniformBytes<64> + Ord,
181 Assigned: InnerConstants,
182 ZeroChip:
183 ZeroInstructions<F, Assigned> + AssignmentInstructions<F, Assigned> + FromScratch<F>,
184 {
185 let circuit = TestCircuit::<F, Assigned, ZeroChip> {
186 x: x.clone(),
187 expected,
188 operation,
189 _marker: PhantomData,
190 };
191
192 let log2_nb_rows = 10;
193 let public_inputs = vec![vec![], vec![]];
194 match MockProver::run(log2_nb_rows, &circuit, public_inputs) {
195 Ok(prover) => match prover.verify() {
196 Ok(()) => assert!(must_pass),
197 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
198 },
199 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
200 }
201
202 if cost_model {
203 circuit_to_json(chip_name, op_name, circuit);
204 }
205 }
206
207 pub fn test_zero_assertions<F, Assigned, ZeroChip>(name: &str)
208 where
209 F: PrimeField + FromUniformBytes<64> + Ord,
210 Assigned: InnerConstants + Sampleable,
211 ZeroChip:
212 ZeroInstructions<F, Assigned> + AssignmentInstructions<F, Assigned> + FromScratch<F>,
213 {
214 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
215 let mut cost_model = true;
216 [
217 (Assigned::sample_inner(&mut rng), false),
218 (Assigned::inner_zero(), true),
219 (Assigned::inner_one(), false),
220 ]
221 .into_iter()
222 .for_each(|(x, is_zero)| {
223 run::<F, Assigned, ZeroChip>(
224 &x,
225 None,
226 Operation::Assert,
227 is_zero,
228 cost_model,
229 name,
230 "assert_zero",
231 );
232 run::<F, Assigned, ZeroChip>(
233 &x,
234 None,
235 Operation::AssertNon,
236 !is_zero,
237 cost_model,
238 name,
239 "assert_non_zero",
240 );
241 cost_model = false;
242 });
243 }
244
245 pub fn test_is_zero<F, Assigned, ZeroChip>(name: &str)
246 where
247 F: PrimeField + FromUniformBytes<64> + Ord,
248 Assigned: InnerConstants + Sampleable,
249 ZeroChip:
250 ZeroInstructions<F, Assigned> + AssignmentInstructions<F, Assigned> + FromScratch<F>,
251 {
252 let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
253 let mut cost_model = true;
254 [
255 (Assigned::sample_inner(&mut rng), false),
256 (Assigned::inner_zero(), true),
257 (Assigned::inner_one(), false),
258 ]
259 .into_iter()
260 .for_each(|(x, expected)| {
261 run::<F, Assigned, ZeroChip>(
262 &x,
263 Some(expected),
264 Operation::IsZero,
265 true,
266 cost_model,
267 name,
268 "is_zero",
269 );
270 run::<F, Assigned, ZeroChip>(
271 &x,
272 Some(!expected),
273 Operation::IsZero,
274 false,
275 false,
276 "",
277 "",
278 );
279 cost_model = false;
280 });
281 }
282}