binius_m3/builder/
test_utils.rs

1// Copyright 2025 Irreducible Inc.
2
3//! Utilities for testing M3 constraint systems and gadgets.
4use anyhow::Result;
5use binius_compute::ComputeHolder;
6use binius_core::{constraint_system::channel::Boundary, fiat_shamir::HasherChallenger};
7use binius_fast_compute::layer::FastCpuLayerHolder;
8use binius_field::{
9	BinaryField128bPolyval, PackedField, PackedFieldIndexable, TowerField,
10	as_packed_field::{PackScalar, PackedType},
11	linear_transformation::PackedTransformationFactory,
12	tower::CanonicalTowerFamily,
13	underlier::UnderlierType,
14};
15use binius_hash::groestl::{Groestl256, Groestl256ByteCompression};
16use binius_utils::env::boolean_env_flag_set;
17
18use super::{
19	B1, B8, B16, B32, B64,
20	constraint_system::ConstraintSystem,
21	table::TableId,
22	witness::{TableFiller, TableWitnessSegment},
23};
24use crate::builder::{B128, WitnessIndex};
25
26/// An easy-to-use implementation of [`TableFiller`] that is constructed with a closure.
27///
28/// Using this [`TableFiller`] implementation carries some overhead, so it is best to use it only
29/// for testing.
30#[allow(clippy::type_complexity)]
31pub struct ClosureFiller<'a, P, Event>
32where
33	P: PackedField,
34	P::Scalar: TowerField,
35{
36	table_id: TableId,
37	fill:
38		Box<dyn for<'b> Fn(&'b [Event], &'b mut TableWitnessSegment<P>) -> Result<()> + Sync + 'a>,
39}
40
41impl<'a, P: PackedField<Scalar: TowerField>, Event> ClosureFiller<'a, P, Event> {
42	pub fn new(
43		table_id: TableId,
44		fill: impl for<'b> Fn(&'b [Event], &'b mut TableWitnessSegment<P>) -> Result<()> + Sync + 'a,
45	) -> Self {
46		Self {
47			table_id,
48			fill: Box::new(fill),
49		}
50	}
51}
52
53impl<P: PackedField<Scalar: TowerField>, Event: Clone> TableFiller<P>
54	for ClosureFiller<'_, P, Event>
55{
56	type Event = Event;
57
58	fn id(&self) -> TableId {
59		self.table_id
60	}
61
62	fn fill(&self, rows: &[Event], witness: &mut TableWitnessSegment<P>) -> Result<()> {
63		(*self.fill)(rows, witness)
64	}
65}
66
67/// Utility for M3 tests to validate a constraint system and witness.
68pub fn validate_system_witness<U>(
69	cs: &ConstraintSystem<B128>,
70	witness: WitnessIndex<PackedType<U, B128>>,
71	boundaries: Vec<Boundary<B128>>,
72) where
73	U: UnderlierType
74		+ PackScalar<B1>
75		+ PackScalar<B8>
76		+ PackScalar<B16>
77		+ PackScalar<B32>
78		+ PackScalar<B64>
79		+ PackScalar<B128>
80		+ PackScalar<BinaryField128bPolyval>,
81	PackedType<U, B128>:
82		PackedFieldIndexable + PackedTransformationFactory<PackedType<U, BinaryField128bPolyval>>,
83	PackedType<U, BinaryField128bPolyval>: PackedTransformationFactory<PackedType<U, B128>>,
84{
85	const TEST_PROVE_VERIFY_ENV_NAME: &str = "BINIUS_M3_TEST_PROVE_VERIFY";
86	validate_system_witness_with_prove_verify::<U>(
87		cs,
88		witness,
89		boundaries,
90		boolean_env_flag_set(TEST_PROVE_VERIFY_ENV_NAME),
91	)
92}
93
94pub fn validate_system_witness_with_prove_verify<U>(
95	cs: &ConstraintSystem<B128>,
96	witness: WitnessIndex<PackedType<U, B128>>,
97	boundaries: Vec<Boundary<B128>>,
98	prove_verify: bool,
99) where
100	U: UnderlierType
101		+ PackScalar<B1>
102		+ PackScalar<B8>
103		+ PackScalar<B16>
104		+ PackScalar<B32>
105		+ PackScalar<B64>
106		+ PackScalar<B128>
107		+ PackScalar<BinaryField128bPolyval>,
108	PackedType<U, B128>:
109		PackedFieldIndexable + PackedTransformationFactory<PackedType<U, BinaryField128bPolyval>>,
110	PackedType<U, BinaryField128bPolyval>: PackedTransformationFactory<PackedType<U, B128>>,
111{
112	let table_sizes = witness.table_sizes();
113	let ccs = cs.compile().unwrap();
114	let witness = witness.into_multilinear_extension_index();
115
116	binius_core::constraint_system::validate::validate_witness(
117		&ccs,
118		&boundaries,
119		&table_sizes,
120		&witness,
121	)
122	.unwrap();
123
124	if prove_verify {
125		const LOG_INV_RATE: usize = 1;
126		const SECURITY_BITS: usize = 100;
127
128		let mut compute_holder =
129			FastCpuLayerHolder::<CanonicalTowerFamily, PackedType<U, B128>>::new(1 << 16, 1 << 24);
130
131		let ccs_digest = ccs.digest::<Groestl256>();
132		let proof = binius_core::constraint_system::prove::<
133			_,
134			U,
135			CanonicalTowerFamily,
136			Groestl256,
137			Groestl256ByteCompression,
138			HasherChallenger<Groestl256>,
139			_,
140			_,
141			_,
142		>(
143			&mut compute_holder.to_data(),
144			&ccs,
145			LOG_INV_RATE,
146			SECURITY_BITS,
147			&ccs_digest,
148			&boundaries,
149			&table_sizes,
150			witness,
151			&binius_hal::make_portable_backend(),
152		)
153		.unwrap();
154
155		binius_core::constraint_system::verify::<
156			U,
157			CanonicalTowerFamily,
158			Groestl256,
159			Groestl256ByteCompression,
160			HasherChallenger<Groestl256>,
161		>(&ccs, LOG_INV_RATE, SECURITY_BITS, &ccs_digest, &boundaries, proof)
162		.unwrap();
163	}
164}