1#![allow(clippy::module_inception)]
10
11pub mod arithmetic;
12pub mod bitwise;
13pub mod blake3;
14pub mod builder;
15pub mod collatz;
16pub mod keccakf;
17pub mod lasso;
18mod pack;
19pub mod plain_lookup;
20pub mod sha256;
21pub mod transparent;
22pub mod u32fib;
23pub mod unconstrained;
24pub mod vision;
25
26#[cfg(test)]
27mod tests {
28 use binius_core::{
29 constraint_system::{
30 self,
31 channel::{validate_witness, Boundary, FlushDirection, OracleOrConst},
32 },
33 fiat_shamir::HasherChallenger,
34 oracle::ShiftVariant,
35 polynomial::ArithCircuitPoly,
36 tower::CanonicalTowerFamily,
37 };
38 use binius_field::{
39 arch::OptimalUnderlier, as_packed_field::PackedType, underlier::WithUnderlier,
40 BinaryField128b, BinaryField1b, BinaryField64b, BinaryField8b, Field, TowerField,
41 };
42 use binius_hal::make_portable_backend;
43 use binius_hash::groestl::{Groestl256, Groestl256ByteCompression};
44 use binius_macros::arith_expr;
45 use binius_math::CompositionPoly;
46 use rand::{seq::SliceRandom, thread_rng};
47
48 type B128 = BinaryField128b;
49 type B64 = BinaryField64b;
50
51 use crate::{
52 builder::{
53 test_utils::test_circuit,
54 types::{F, U},
55 ConstraintSystemBuilder,
56 },
57 unconstrained::unconstrained,
58 };
59
60 #[test]
61 fn test_boundaries() {
62 let allocator = bumpalo::Bump::new();
64 let mut builder = ConstraintSystemBuilder::new_with_witness(&allocator);
65
66 let log_size = PackedType::<U, BinaryField8b>::LOG_WIDTH + 2;
67
68 let channel_id = builder.add_channel();
69
70 let push_boundaries = Boundary {
71 values: vec![F::from_underlier(6)],
72 channel_id,
73 direction: FlushDirection::Push,
74 multiplicity: 1,
75 };
76
77 let pull_boundaries = Boundary {
78 values: vec![F::ONE],
79 channel_id,
80 direction: FlushDirection::Pull,
81 multiplicity: 1,
82 };
83
84 let boundaries = vec![pull_boundaries, push_boundaries];
85
86 let even = builder.add_committed("even", log_size, 3);
87
88 let half = builder.add_committed("half", log_size, 3);
89
90 let odd = builder.add_committed("odd", log_size, 3);
91
92 let output = builder.add_committed("output", log_size, 3);
93
94 let mut even_counter = 0;
95
96 let mut odd_counter = 0;
97
98 if let Some(witness) = builder.witness() {
99 let mut current = 6;
100
101 let mut even = witness.new_column::<BinaryField8b>(even);
102
103 let even_u8 = even.as_mut_slice::<u8>();
104
105 let mut half = witness.new_column::<BinaryField8b>(half);
106
107 let half_u8 = half.as_mut_slice::<u8>();
108
109 let mut odd = witness.new_column::<BinaryField8b>(odd);
110
111 let odd_u8 = odd.as_mut_slice::<u8>();
112
113 let mut output = witness.new_column::<BinaryField8b>(output);
114
115 let output_u8 = output.as_mut_slice::<u8>();
116
117 while current != 1 {
118 if current & 1 == 0 {
119 even_u8[even_counter] = current;
120 half_u8[even_counter] = current / 2;
121 current = half_u8[even_counter];
122 even_counter += 1;
123 } else {
124 odd_u8[odd_counter] = current;
125 output_u8[odd_counter] = 3 * current + 1;
126 current = output_u8[odd_counter];
127 odd_counter += 1;
128 }
129 }
130 }
131
132 builder
133 .flush(FlushDirection::Pull, channel_id, even_counter, [OracleOrConst::Oracle(even)])
134 .unwrap();
135 builder
136 .flush(FlushDirection::Push, channel_id, even_counter, [OracleOrConst::Oracle(half)])
137 .unwrap();
138 builder
139 .flush(FlushDirection::Pull, channel_id, odd_counter, [OracleOrConst::Oracle(odd)])
140 .unwrap();
141 builder
142 .flush(FlushDirection::Push, channel_id, odd_counter, [OracleOrConst::Oracle(output)])
143 .unwrap();
144
145 let witness = builder
146 .take_witness()
147 .expect("builder created with witness");
148
149 let constraint_system = builder.build().unwrap();
150
151 let backend = make_portable_backend();
152
153 let proof = constraint_system::prove::<
154 U,
155 CanonicalTowerFamily,
156 Groestl256,
157 Groestl256ByteCompression,
158 HasherChallenger<Groestl256>,
159 _,
160 >(&constraint_system, 1, 10, &boundaries, witness, &backend)
161 .unwrap();
162
163 constraint_system::verify::<
164 U,
165 CanonicalTowerFamily,
166 Groestl256,
167 Groestl256ByteCompression,
168 HasherChallenger<Groestl256>,
169 >(&constraint_system, 1, 10, &boundaries, proof)
170 .unwrap();
171 }
172
173 #[test]
174 #[ignore]
175 fn test_composite_circuit() {
176 let backend = make_portable_backend();
177 let allocator = bumpalo::Bump::new();
178 let mut builder = ConstraintSystemBuilder::new_with_witness(&allocator);
179 let n_vars = 8;
180 let log_inv_rate = 1;
181 let security_bits = 30;
182 let comp_1 = arith_expr!(B128[x, y] = x*y*y*0x85 +x*x*y*0x9 + y + 0x123);
183 let comp_2 =
184 arith_expr!(B128[x, y, z] = x*z*y*0x81115 +x*y*0x98888 + y*z + z*z*z*z*z*z + 0x155523);
185 let comp_3 = arith_expr!(B128[a, b, c, d, e, f] = e*f*f + a*b*c*2 + d*0x999 + 0x123);
186 let comp_4 = arith_expr!(B128[a, b] = a*(b+a));
187
188 let column_x = builder.add_committed("x", n_vars, 7);
189 let column_y = builder.add_committed("y", n_vars, 7);
190 let column_comp_1 = builder
191 .add_composite_mle("comp1", n_vars, [column_x, column_y], comp_1.clone())
192 .unwrap();
193
194 let column_shift = builder
195 .add_shifted(
196 "shift",
197 column_comp_1,
198 (1 << n_vars) - 1,
199 n_vars,
200 ShiftVariant::CircularLeft,
201 )
202 .unwrap();
203
204 let column_comp_2 = builder
205 .add_composite_mle(
206 "comp2",
207 n_vars,
208 [column_y, column_comp_1, column_shift],
209 comp_2.clone(),
210 )
211 .unwrap();
212
213 let column_z = builder.add_committed("z", n_vars + 1, 6);
214 let column_packed = builder.add_packed("packed", column_z, 1).unwrap();
215
216 let column_comp_3 = builder
217 .add_composite_mle(
218 "comp3",
219 n_vars,
220 [
221 column_x,
222 column_x,
223 column_comp_1,
224 column_shift,
225 column_comp_2,
226 column_packed,
227 ],
228 comp_3.clone(),
229 )
230 .unwrap();
231
232 let column_comp_4 = builder
233 .add_composite_mle(
234 "comp4",
235 n_vars,
236 [
237 column_comp_2,
238 column_comp_3,
239 column_x,
240 column_shift,
241 column_y,
242 ],
243 comp_4.clone(),
244 )
245 .unwrap();
246
247 let channel = builder.add_channel();
249 builder
250 .send(
251 channel,
252 1 << n_vars,
253 vec![
254 OracleOrConst::Oracle(column_y),
255 OracleOrConst::Oracle(column_x),
256 OracleOrConst::Oracle(column_comp_1),
257 OracleOrConst::Oracle(column_shift),
258 OracleOrConst::Oracle(column_comp_2),
259 OracleOrConst::Oracle(column_packed),
260 OracleOrConst::Oracle(column_comp_3),
261 ],
262 )
263 .unwrap();
264 builder
265 .receive(
266 channel,
267 1 << n_vars,
268 vec![
269 OracleOrConst::Oracle(column_x),
270 OracleOrConst::Oracle(column_y),
271 OracleOrConst::Oracle(column_comp_1),
272 OracleOrConst::Oracle(column_shift),
273 OracleOrConst::Oracle(column_comp_2),
274 OracleOrConst::Oracle(column_packed),
275 OracleOrConst::Oracle(column_comp_3),
276 ],
277 )
278 .unwrap();
279
280 let values_x = (0..(1 << n_vars))
281 .map(|i| B128::from(i as u128))
282 .collect::<Vec<_>>();
283 let values_y = (0..(1 << n_vars))
284 .map(|i| B128::from(i * i))
285 .collect::<Vec<_>>();
286
287 let arith_poly_1 = ArithCircuitPoly::new(comp_1);
288 let values_comp_1 = (0..(1 << n_vars))
289 .map(|i| arith_poly_1.evaluate(&[values_x[i], values_y[i]]).unwrap())
290 .collect::<Vec<_>>();
291
292 let mut values_shift = values_comp_1.clone();
293 let first = values_shift.remove(0);
294 values_shift.push(first);
295
296 let arith_poly_2 = ArithCircuitPoly::new(comp_2);
297 let values_comp_2 = (0..(1 << n_vars))
298 .map(|i| {
299 arith_poly_2
300 .evaluate(&[values_y[i], values_comp_1[i], values_shift[i]])
301 .unwrap()
302 })
303 .collect::<Vec<_>>();
304
305 let values_z = (0..(1 << (n_vars + 1)))
306 .map(|i| B64::from(i * i / 8 + i % 10_u64))
307 .collect::<Vec<_>>();
308 let values_packed = (0..(1 << n_vars))
309 .map(|i| {
310 B128::from(
311 ((values_z[2 * i + 1].val() as u128) << 64) + values_z[2 * i].val() as u128,
312 )
313 })
314 .collect::<Vec<_>>();
315
316 let arith_poly_3 = ArithCircuitPoly::new(comp_3);
317 let values_comp_3 = (0..(1 << n_vars))
318 .map(|i| {
319 arith_poly_3
320 .evaluate(&[
321 values_x[i],
322 values_x[i],
323 values_comp_1[i],
324 values_shift[i],
325 values_comp_2[i],
326 values_packed[i],
327 ])
328 .unwrap()
329 })
330 .collect::<Vec<_>>();
331
332 let arith_poly_4 = ArithCircuitPoly::new(comp_4);
333 let values_comp_4 = (0..(1 << n_vars))
334 .map(|i| {
335 arith_poly_4
336 .evaluate(&[values_comp_2[i], values_comp_3[i]])
337 .unwrap()
338 })
339 .collect::<Vec<_>>();
340
341 let mut add_witness_col_b128 = |oracle_id: usize, values: &[B128]| {
342 builder
343 .witness()
344 .unwrap()
345 .new_column::<B128>(oracle_id)
346 .as_mut_slice()
347 .copy_from_slice(values);
348 };
349 add_witness_col_b128(column_x, &values_x);
350 add_witness_col_b128(column_y, &values_y);
351 add_witness_col_b128(column_comp_1, &values_comp_1);
352 add_witness_col_b128(column_shift, &values_shift);
353 add_witness_col_b128(column_comp_2, &values_comp_2);
354 add_witness_col_b128(column_packed, &values_packed);
355 add_witness_col_b128(column_comp_3, &values_comp_3);
356 add_witness_col_b128(column_comp_4, &values_comp_4);
357 builder
358 .witness()
359 .unwrap()
360 .new_column::<B64>(column_z)
361 .as_mut_slice()
362 .copy_from_slice(&values_z);
363
364 let witness = builder.take_witness().unwrap();
365 let constraint_system = builder.build().unwrap();
366
367 validate_witness(&witness, &[], &[], 1).unwrap();
368
369 let proof = binius_core::constraint_system::prove::<
370 OptimalUnderlier,
371 CanonicalTowerFamily,
372 Groestl256,
373 Groestl256ByteCompression,
374 HasherChallenger<Groestl256>,
375 _,
376 >(&constraint_system, log_inv_rate, security_bits, &[], witness, &backend)
377 .unwrap();
378
379 binius_core::constraint_system::verify::<
380 OptimalUnderlier,
381 CanonicalTowerFamily,
382 Groestl256,
383 Groestl256ByteCompression,
384 HasherChallenger<Groestl256>,
385 >(&constraint_system, log_inv_rate, security_bits, &[], proof)
386 .unwrap();
387 }
388
389 #[test]
390 fn test_flush_with_const() {
391 test_circuit(|builder| {
392 let channel_id = builder.add_channel();
393 let oracle = unconstrained::<BinaryField1b>(builder, "oracle", 1)?;
394 builder
395 .flush(
396 FlushDirection::Push,
397 channel_id,
398 1,
399 vec![
400 OracleOrConst::Oracle(oracle),
401 OracleOrConst::Const {
402 base: F::ONE,
403 tower_level: BinaryField1b::TOWER_LEVEL,
404 },
405 ],
406 )
407 .unwrap();
408
409 builder
410 .flush(
411 FlushDirection::Pull,
412 channel_id,
413 1,
414 vec![
415 OracleOrConst::Oracle(oracle),
416 OracleOrConst::Const {
417 base: F::ONE,
418 tower_level: BinaryField1b::TOWER_LEVEL,
419 },
420 ],
421 )
422 .unwrap();
423
424 Ok(vec![])
425 })
426 .unwrap()
427 }
428
429 #[test]
431 fn test_flush_with_const_large() {
432 test_circuit(|builder| {
433 let channel_id = builder.add_channel();
434 let mut rng = thread_rng();
435 let oracles = (0..5)
436 .map(|i| unconstrained::<BinaryField128b>(builder, format!("oracle {i}"), 5))
437 .collect::<Result<Vec<_>, _>>()?;
438 let random_consts = (0..5).map(|_| OracleOrConst::Const {
439 base: BinaryField128b::random(&mut rng),
440 tower_level: BinaryField128b::TOWER_LEVEL,
441 });
442 let mut random_order = oracles
445 .iter()
446 .copied()
447 .map(OracleOrConst::Oracle)
448 .chain(random_consts)
449 .collect::<Vec<_>>();
450 random_order.shuffle(&mut rng);
451
452 let random_order_iterator = random_order.iter().copied();
453 for i in 0..1 << 5 {
454 builder
455 .flush(FlushDirection::Push, channel_id, i, random_order_iterator.clone())
456 .unwrap();
457
458 builder
459 .flush(FlushDirection::Pull, channel_id, i, random_order_iterator.clone())
460 .unwrap();
461 }
462
463 Ok(vec![])
464 })
465 .unwrap()
466 }
467}