binius_circuits/
lib.rs

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