binius_spartan_verifier/
constraint_system.rs1use binius_field::Field;
5use binius_ip::mlecheck::mask_buffer_dimensions;
6pub use binius_spartan_frontend::constraint_system::BlindingInfo;
7use binius_spartan_frontend::constraint_system::{
8 ConstraintSystem, MulConstraint, Operand, Witness, WitnessIndex,
9};
10use binius_utils::checked_arithmetics::{checked_log_2, log2_ceil_usize};
11
12#[derive(Debug, Clone)]
18pub struct ConstraintSystemPadded<F: Field> {
19 inner: ConstraintSystem<F>,
20 log_precommit: u32,
21 log_private: u32,
22 blinding_info: BlindingInfo,
23 mul_constraints: Vec<MulConstraint<WitnessIndex>>,
24 mask_dims: (usize, usize),
26}
27
28impl<F: Field> ConstraintSystemPadded<F> {
29 pub fn new(cs: ConstraintSystem<F>, blinding_info: BlindingInfo) -> Self {
37 let mut mul_constraints = cs.mul_constraints().to_vec();
38
39 fn add_blinding_constraints(
41 mul_constraints: &mut Vec<MulConstraint<WitnessIndex>>,
42 make_index: fn(u32) -> WitnessIndex,
43 n_circuit_wires: usize,
44 n_dummy_wires: usize,
45 n_dummy_constraints: usize,
46 ) -> u32 {
47 let dummy_base = n_circuit_wires + n_dummy_wires;
48 for i in 0..n_dummy_constraints {
49 let a = make_index((dummy_base + 3 * i) as u32);
50 let b = make_index((dummy_base + 3 * i + 1) as u32);
51 let c = make_index((dummy_base + 3 * i + 2) as u32);
52 mul_constraints.push(MulConstraint {
53 a: Operand::from(a),
54 b: Operand::from(b),
55 c: Operand::from(c),
56 });
57 }
58
59 let blinding_size = n_dummy_wires + 3 * n_dummy_constraints;
60 log2_ceil_usize(n_circuit_wires + blinding_size) as u32
61 }
62
63 let log_precommit = add_blinding_constraints(
66 &mut mul_constraints,
67 WitnessIndex::precommit,
68 cs.n_precommit() as usize,
69 blinding_info.n_dummy_wires,
70 blinding_info.n_dummy_constraints,
71 );
72 let log_private = add_blinding_constraints(
73 &mut mul_constraints,
74 WitnessIndex::private,
75 cs.n_private() as usize,
76 blinding_info.n_dummy_wires,
77 blinding_info.n_dummy_constraints,
78 );
79
80 let one_operand = Operand::from(cs.one_wire());
82 let current_len = mul_constraints.len();
83 mul_constraints.resize(
84 current_len.next_power_of_two(),
85 MulConstraint {
86 a: one_operand.clone(),
87 b: one_operand.clone(),
88 c: one_operand,
89 },
90 );
91
92 let log_mul_constraints = checked_log_2(mul_constraints.len());
94 let mask_degree = 2; let mask_dims =
96 mask_buffer_dimensions(log_mul_constraints, mask_degree, blinding_info.n_dummy_wires);
97
98 Self {
99 inner: cs,
100 log_precommit,
101 log_private,
102 blinding_info,
103 mul_constraints,
104 mask_dims,
105 }
106 }
107
108 pub fn constants(&self) -> &[F] {
109 self.inner.constants()
110 }
111
112 pub const fn n_inout(&self) -> u32 {
113 self.inner.n_inout()
114 }
115
116 pub const fn n_precommit(&self) -> u32 {
117 self.inner.n_precommit()
118 }
119
120 pub const fn n_private(&self) -> u32 {
121 self.inner.n_private()
122 }
123
124 pub const fn log_public(&self) -> u32 {
125 self.inner.log_public()
126 }
127
128 pub const fn n_public(&self) -> u32 {
129 self.inner.n_public()
130 }
131
132 pub const fn one_wire(&self) -> WitnessIndex {
133 self.inner.one_wire()
134 }
135
136 pub const fn log_precommit(&self) -> u32 {
137 self.log_precommit
138 }
139
140 pub const fn precommit_size(&self) -> usize {
141 1 << self.log_precommit as usize
142 }
143
144 pub const fn log_private(&self) -> u32 {
145 self.log_private
146 }
147
148 pub const fn private_size(&self) -> usize {
149 1 << self.log_private as usize
150 }
151
152 pub const fn blinding_info(&self) -> &BlindingInfo {
153 &self.blinding_info
154 }
155
156 pub fn mul_constraints(&self) -> &[MulConstraint<WitnessIndex>] {
157 &self.mul_constraints
158 }
159
160 pub const fn mask_dims(&self) -> (usize, usize) {
162 self.mask_dims
163 }
164
165 pub fn validate(&self, witness: &Witness<F>) {
166 assert_eq!(witness.public().len(), 1 << self.log_public() as usize);
167 assert_eq!(witness.private().len(), self.private_size());
168
169 let operand_val = |operand: &Operand<WitnessIndex>| {
170 operand.wires().iter().map(|&idx| witness[idx]).sum::<F>()
171 };
172
173 for MulConstraint { a, b, c } in &self.mul_constraints {
174 assert_eq!(operand_val(a) * operand_val(b), operand_val(c));
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use std::collections::BTreeSet;
182
183 use binius_field::Ghash128b as B128;
184 use binius_spartan_frontend::{
185 circuit_builder::{CircuitBuilder, ConstraintBuilder},
186 compiler::compile,
187 constraint_system::WitnessSegment,
188 };
189
190 use super::*;
191
192 #[test]
193 fn every_committed_segment_reserves_one_wire_beyond_the_fri_queries() {
194 const N_TEST_QUERIES: usize = 32;
196
197 let mut builder = ConstraintBuilder::<B128>::new();
199 let x = builder.alloc_inout();
200 let y = builder.alloc_inout();
201 builder.assert_eq(x, y);
202 let (cs, _layout) = compile(builder);
203
204 let n_precommit = cs.n_precommit() as usize;
205 let n_private = cs.n_private() as usize;
206
207 let info = BlindingInfo::for_fri_queries(N_TEST_QUERIES);
208 let padded = ConstraintSystemPadded::new(cs, info);
209
210 assert!(info.n_dummy_wires > N_TEST_QUERIES);
214
215 let blinding = info.n_dummy_wires + 3 * info.n_dummy_constraints;
220 assert!(padded.precommit_size() >= n_precommit + blinding);
221 assert!(padded.private_size() >= n_private + blinding);
222 }
223
224 #[test]
225 fn every_committed_segment_masks_its_revealed_evaluations() {
226 const N_TEST_QUERIES: usize = 8;
236
237 let mut builder = ConstraintBuilder::<B128>::new();
238 let x = builder.alloc_inout();
239 let y = builder.alloc_inout();
240 builder.assert_eq(x, y);
241 let (cs, _layout) = compile(builder);
242
243 let n_circuit = [cs.n_precommit() as usize, cs.n_private() as usize];
244 let info = BlindingInfo::for_fri_queries(N_TEST_QUERIES);
245 let padded = ConstraintSystemPadded::new(cs, info);
246
247 let mut in_support = [BTreeSet::new(), BTreeSet::new()];
249 for constraint in padded.mul_constraints() {
250 for operand in [&constraint.a, &constraint.b, &constraint.c] {
251 for wire in operand.wires() {
252 let slot = match wire.segment {
253 WitnessSegment::Precommit => 0,
254 WitnessSegment::Private => 1,
255 WitnessSegment::Public => continue,
256 };
257 in_support[slot].insert(wire.index as usize);
258 }
259 }
260 }
261
262 for (segment, n_circuit) in in_support.iter().zip(n_circuit) {
263 for offset in 0..info.n_dummy_wires {
266 assert!(
267 !segment.contains(&(n_circuit + offset)),
268 "a dummy wire reached the wiring relation"
269 );
270 }
271
272 let dummy_constraint_base = n_circuit + info.n_dummy_wires;
275 for offset in 0..3 * info.n_dummy_constraints {
276 assert!(
277 segment.contains(&(dummy_constraint_base + offset)),
278 "a dummy constraint wire never reached the wiring relation"
279 );
280 }
281 }
282 }
283}