1use midnight_proofs::{circuit::Layouter, plonk::Error};
19
20use crate::{types::AssignedBit, CircuitField};
21
22pub trait BinaryInstructions<F: CircuitField> {
24 fn and(
46 &self,
47 layouter: &mut impl Layouter<F>,
48 bits: &[AssignedBit<F>],
49 ) -> Result<AssignedBit<F>, Error>;
50
51 fn or(
67 &self,
68 layouter: &mut impl Layouter<F>,
69 bits: &[AssignedBit<F>],
70 ) -> Result<AssignedBit<F>, Error>;
71
72 fn xor(
89 &self,
90 layouter: &mut impl Layouter<F>,
91 bits: &[AssignedBit<F>],
92 ) -> Result<AssignedBit<F>, Error>;
93
94 fn not(
105 &self,
106 layouter: &mut impl Layouter<F>,
107 bit: &AssignedBit<F>,
108 ) -> Result<AssignedBit<F>, Error>;
109}
110
111#[cfg(test)]
112pub(crate) mod tests {
113 use std::{cmp::min, marker::PhantomData};
114
115 use ff::FromUniformBytes;
116 use midnight_proofs::{
117 circuit::{Layouter, SimpleFloorPlanner, Value},
118 dev::MockProver,
119 plonk::{Circuit, ConstraintSystem},
120 };
121
122 use super::*;
123 use crate::{
124 instructions::{AssertionInstructions, AssignmentInstructions},
125 testing_utils::FromScratch,
126 utils::circuit_modeling::{circuit_to_json, cost_measure_end, cost_measure_start},
127 };
128
129 #[derive(Clone, Copy, Debug)]
130 enum Operation {
131 And,
132 Or,
133 Xor,
134 Not,
135 }
136
137 #[derive(Clone, Debug)]
138 struct TestCircuit<F, BinaryChip> {
139 inputs: Vec<bool>,
140 expected: bool,
141 operation: Operation,
142 _marker: PhantomData<(F, BinaryChip)>,
143 }
144
145 impl<F, BinaryChip> Circuit<F> for TestCircuit<F, BinaryChip>
146 where
147 F: CircuitField,
148 BinaryChip: BinaryInstructions<F>
149 + AssignmentInstructions<F, AssignedBit<F>>
150 + AssertionInstructions<F, AssignedBit<F>>
151 + FromScratch<F>,
152 {
153 type Config = <BinaryChip as FromScratch<F>>::Config;
154 type FloorPlanner = SimpleFloorPlanner;
155 type Params = ();
156
157 fn without_witnesses(&self) -> Self {
158 unreachable!()
159 }
160
161 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
162 let committed_instance_column = meta.instance_column();
163 let instance_column = meta.instance_column();
164 BinaryChip::configure_from_scratch(
165 meta,
166 &mut vec![],
167 &mut vec![],
168 &[committed_instance_column, instance_column],
169 )
170 }
171
172 fn synthesize(
173 &self,
174 config: Self::Config,
175 mut layouter: impl Layouter<F>,
176 ) -> Result<(), Error> {
177 let chip = BinaryChip::new_from_scratch(&config);
178
179 let b2_idx = min(self.inputs.len() - 1, 1);
181 let b1 = chip.assign(&mut layouter, Value::known(self.inputs[0]))?;
182 let b2 = chip.assign(&mut layouter, Value::known(self.inputs[b2_idx]))?;
183
184 cost_measure_start(&mut layouter);
185 let res = match self.operation {
186 Operation::And => chip.and(&mut layouter, &[b1, b2]),
187 Operation::Or => chip.or(&mut layouter, &[b1, b2]),
188 Operation::Xor => chip.xor(&mut layouter, &[b1, b2]),
189 Operation::Not => chip.not(&mut layouter, &b1),
190 }?;
191 cost_measure_end(&mut layouter);
192
193 chip.assert_equal_to_fixed(&mut layouter, &res, self.expected)?;
194
195 chip.load_from_scratch(&mut layouter)
196 }
197 }
198
199 fn run<F, BinaryChip>(
200 inputs: &[bool],
201 expected: bool,
202 operation: Operation,
203 must_pass: bool,
204 cost_model: bool,
205 chip_name: &str,
206 op_name: &str,
207 ) where
208 F: CircuitField + FromUniformBytes<64> + Ord,
209 BinaryChip: BinaryInstructions<F>
210 + AssignmentInstructions<F, AssignedBit<F>>
211 + AssertionInstructions<F, AssignedBit<F>>
212 + FromScratch<F>,
213 {
214 let circuit = TestCircuit::<F, BinaryChip> {
215 inputs: inputs.to_vec(),
216 expected,
217 operation,
218 _marker: PhantomData,
219 };
220 let public_inputs = vec![vec![], vec![]];
221 match MockProver::run(&circuit, public_inputs) {
222 Ok(prover) => match prover.verify() {
223 Ok(()) => assert!(must_pass),
224 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
225 },
226 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
227 }
228
229 if cost_model {
230 circuit_to_json(chip_name, op_name, circuit);
231 }
232 }
233
234 fn test_binary_op<F, BinaryChip>(
235 inputs: &[u8],
236 expected: u8,
237 operation: Operation,
238 cost_model: bool,
239 chip_name: &str,
240 op_name: &str,
241 ) where
242 F: CircuitField + FromUniformBytes<64> + Ord,
243 BinaryChip: BinaryInstructions<F>
244 + AssignmentInstructions<F, AssignedBit<F>>
245 + AssertionInstructions<F, AssignedBit<F>>
246 + FromScratch<F>,
247 {
248 let inputs = inputs.iter().map(|b| *b == 1).collect::<Vec<_>>();
249 let expected = expected == 1;
250 run::<F, BinaryChip>(
251 &inputs, expected, operation, true, cost_model, chip_name, op_name,
252 );
253 run::<F, BinaryChip>(
254 &inputs, !expected, operation, false, false, chip_name, op_name,
255 );
256 }
257
258 pub fn test_and<F, BinaryChip>(name: &str)
259 where
260 F: CircuitField + FromUniformBytes<64> + Ord,
261 BinaryChip: BinaryInstructions<F>
262 + AssignmentInstructions<F, AssignedBit<F>>
263 + AssertionInstructions<F, AssignedBit<F>>
264 + FromScratch<F>,
265 {
266 test_binary_op::<F, BinaryChip>(&[0, 0], 0, Operation::And, true, name, "and");
267 test_binary_op::<F, BinaryChip>(&[0, 1], 0, Operation::And, false, "", "");
268 test_binary_op::<F, BinaryChip>(&[1, 0], 0, Operation::And, false, "", "");
269 test_binary_op::<F, BinaryChip>(&[1, 1], 1, Operation::And, false, "", "");
270 }
271
272 pub fn test_or<F, BinaryChip>(name: &str)
273 where
274 F: CircuitField + FromUniformBytes<64> + Ord,
275 BinaryChip: BinaryInstructions<F>
276 + AssignmentInstructions<F, AssignedBit<F>>
277 + AssertionInstructions<F, AssignedBit<F>>
278 + FromScratch<F>,
279 {
280 test_binary_op::<F, BinaryChip>(&[0, 0], 0, Operation::Or, true, name, "or");
281 test_binary_op::<F, BinaryChip>(&[0, 1], 1, Operation::Or, false, "", "");
282 test_binary_op::<F, BinaryChip>(&[1, 0], 1, Operation::Or, false, "", "");
283 test_binary_op::<F, BinaryChip>(&[1, 1], 1, Operation::Or, false, "", "");
284 }
285
286 pub fn test_xor<F, BinaryChip>(name: &str)
287 where
288 F: CircuitField + FromUniformBytes<64> + Ord,
289 BinaryChip: BinaryInstructions<F>
290 + AssignmentInstructions<F, AssignedBit<F>>
291 + AssertionInstructions<F, AssignedBit<F>>
292 + FromScratch<F>,
293 {
294 test_binary_op::<F, BinaryChip>(&[0, 0], 0, Operation::Xor, true, name, "xor");
295 test_binary_op::<F, BinaryChip>(&[0, 1], 1, Operation::Xor, false, "", "");
296 test_binary_op::<F, BinaryChip>(&[1, 0], 1, Operation::Xor, false, "", "");
297 test_binary_op::<F, BinaryChip>(&[1, 1], 0, Operation::Xor, false, "", "");
298 }
299
300 pub fn test_not<F, BinaryChip>(name: &str)
301 where
302 F: CircuitField + FromUniformBytes<64> + Ord,
303 BinaryChip: BinaryInstructions<F>
304 + AssignmentInstructions<F, AssignedBit<F>>
305 + AssertionInstructions<F, AssignedBit<F>>
306 + FromScratch<F>,
307 {
308 test_binary_op::<F, BinaryChip>(&[0], 1, Operation::Not, true, name, "not");
309 test_binary_op::<F, BinaryChip>(&[1], 0, Operation::Not, false, "", "");
310 }
311}