11// Copyright 2026 The Binius Developers
22
3- //! Builder channel that symbolically executes a verifier to build constraint systems.
3+ //! Channels that symbolically execute a verifier to build constraint systems or fill witnesses .
44
55use std:: { cell:: RefCell , rc:: Rc } ;
66
7- use binius_field:: { BinaryField128bGhash as B128 , Field } ;
7+ use binius_field:: Field ;
88use binius_iop:: channel:: { IOPVerifierChannel , OracleLinearRelation , OracleSpec } ;
99use binius_ip:: channel:: IPVerifierChannel ;
1010use binius_spartan_frontend:: {
@@ -14,38 +14,45 @@ use binius_spartan_frontend::{
1414
1515use crate :: circuit_elem:: { CircuitElem , CircuitWire } ;
1616
17- type BuildElem = CircuitElem < ConstraintBuilder < B128 > > ;
18- type BuildWire = CircuitWire < ConstraintBuilder < B128 > > ;
19-
2017/// A channel that symbolically executes a verifier, building up an IronSpartan constraint system.
2118///
2219/// Instead of performing actual verification, this channel records all operations as constraints
23- /// in a [`ConstraintBuilder<B128> `]. The typical usage pattern is:
20+ /// in a [`ConstraintBuilder`]. The typical usage pattern is:
2421///
25- /// 1. Create an `IronSpartanBuilderChannel` from a `ConstraintBuilder<B128>`
22+ /// 1. Create an `IronSpartanBuilderChannel` from a [ `ConstraintBuilder`]
2623/// 2. Run the verifier on the channel (e.g., `verify_iop`)
27- /// 3. The channel's `finish()` method returns the `ConstraintBuilder<B128>` with all recorded
24+ /// 3. The channel's `finish()` method returns the [ `ConstraintBuilder`] with all recorded
2825/// constraints
29- pub struct IronSpartanBuilderChannel {
30- builder : Rc < RefCell < ConstraintBuilder < B128 > > > ,
26+ pub struct IronSpartanBuilderChannel < F : Field > {
27+ builder : Rc < RefCell < ConstraintBuilder < F > > > ,
3128}
3229
33- impl IronSpartanBuilderChannel {
30+ impl < F : Field > IronSpartanBuilderChannel < F > {
3431 /// Creates a new builder channel that takes ownership of the given constraint builder.
35- pub fn new ( builder : ConstraintBuilder < B128 > ) -> Self {
32+ pub fn new ( builder : ConstraintBuilder < F > ) -> Self {
3633 Self {
3734 builder : Rc :: new ( RefCell :: new ( builder) ) ,
3835 }
3936 }
4037
41- fn alloc_inout_elem ( & self ) -> BuildElem {
38+ fn alloc_inout_elem ( & self ) -> CircuitElem < ConstraintBuilder < F > > {
4239 let wire = self . builder . borrow_mut ( ) . alloc_inout ( ) ;
43- BuildElem :: Wire ( BuildWire :: new ( & self . builder , wire) )
40+ CircuitElem :: Wire ( CircuitWire :: new ( & self . builder , wire) )
41+ }
42+
43+ /// Consumes the channel and returns the underlying [`ConstraintBuilder`].
44+ ///
45+ /// This must be called after all `CircuitElem` values derived from this channel have been
46+ /// dropped, as it requires sole ownership of the builder via `Rc::try_unwrap`.
47+ pub fn finish ( self ) -> ConstraintBuilder < F > {
48+ Rc :: try_unwrap ( self . builder )
49+ . expect ( "CircuitElem values should only hold Weak references" )
50+ . into_inner ( )
4451 }
4552}
4653
47- impl IPVerifierChannel < B128 > for IronSpartanBuilderChannel {
48- type Elem = BuildElem ;
54+ impl < F : Field > IPVerifierChannel < F > for IronSpartanBuilderChannel < F > {
55+ type Elem = CircuitElem < ConstraintBuilder < F > > ;
4956
5057 fn recv_one ( & mut self ) -> Result < Self :: Elem , binius_ip:: channel:: Error > {
5158 Ok ( self . alloc_inout_elem ( ) )
@@ -63,29 +70,27 @@ impl IPVerifierChannel<B128> for IronSpartanBuilderChannel {
6370 self . alloc_inout_elem ( )
6471 }
6572
66- fn observe_one ( & mut self , _val : B128 ) -> Self :: Elem {
73+ fn observe_one ( & mut self , _val : F ) -> Self :: Elem {
6774 self . alloc_inout_elem ( )
6875 }
6976
70- fn observe_many ( & mut self , vals : & [ B128 ] ) -> Vec < Self :: Elem > {
77+ fn observe_many ( & mut self , vals : & [ F ] ) -> Vec < Self :: Elem > {
7178 ( 0 ..vals. len ( ) ) . map ( |_| self . alloc_inout_elem ( ) ) . collect ( )
7279 }
7380
7481 fn assert_zero ( & mut self , val : Self :: Elem ) -> Result < ( ) , binius_ip:: channel:: Error > {
75- use binius_spartan_frontend:: circuit_builder:: CircuitBuilder ;
76-
7782 match val {
78- BuildElem :: Constant ( c) if c == B128 :: ZERO => Ok ( ( ) ) ,
79- BuildElem :: Constant ( _) => Err ( binius_ip:: channel:: Error :: InvalidAssert ) ,
80- BuildElem :: Wire ( w) => {
83+ CircuitElem :: Constant ( c) if c == F :: ZERO => Ok ( ( ) ) ,
84+ CircuitElem :: Constant ( _) => Err ( binius_ip:: channel:: Error :: InvalidAssert ) ,
85+ CircuitElem :: Wire ( w) => {
8186 self . builder . borrow_mut ( ) . assert_zero ( w. wire ( ) ) ;
8287 Ok ( ( ) )
8388 }
8489 }
8590 }
8691}
8792
88- impl IOPVerifierChannel < B128 > for IronSpartanBuilderChannel {
93+ impl < F : Field > IOPVerifierChannel < F > for IronSpartanBuilderChannel < F > {
8994 type Oracle = ( ) ;
9095
9196 fn remaining_oracle_specs ( & self ) -> & [ OracleSpec ] {
@@ -104,70 +109,53 @@ impl IOPVerifierChannel<B128> for IronSpartanBuilderChannel {
104109 }
105110}
106111
107- impl IronSpartanBuilderChannel {
108- /// Consumes the channel and returns the underlying [`ConstraintBuilder<B128>`].
109- ///
110- /// This must be called after all `BuildElem` values derived from this channel have been
111- /// dropped, as it requires sole ownership of the builder via `Rc::try_unwrap`.
112- pub fn finish ( self ) -> ConstraintBuilder < B128 > {
113- Rc :: try_unwrap ( self . builder )
114- . expect ( "BuildElem values should only hold Weak references" )
115- . into_inner ( )
116- }
117- }
118-
119- type WitnessElem < ' a > = CircuitElem < WitnessGenerator < ' a , B128 > > ;
120- type WitnessWire < ' a > = CircuitWire < WitnessGenerator < ' a , B128 > > ;
121-
122112/// A channel that replays recorded interaction values through a [`WitnessGenerator`], filling
123113/// both inout and private wires in the outer witness.
124114///
125115/// This mirrors [`IronSpartanBuilderChannel`] but uses concrete evaluation instead of symbolic
126116/// constraint building. Each operation consumes the next value and writes it to the corresponding
127117/// inout wire in the [`WitnessGenerator`]. When the verifier's arithmetic runs on the returned
128118/// [`CircuitElem`] values, the [`WitnessGenerator`] fills private wires.
129- pub struct ReplayChannel < ' a > {
130- witness_gen : Rc < RefCell < WitnessGenerator < ' a , B128 > > > ,
131- events : std:: vec:: IntoIter < B128 > ,
119+ pub struct ReplayChannel < ' a , F : Field > {
120+ witness_gen : Rc < RefCell < WitnessGenerator < ' a , F > > > ,
121+ events : std:: vec:: IntoIter < F > ,
132122 next_inout_id : u32 ,
133123}
134124
135- impl < ' a > ReplayChannel < ' a > {
125+ impl < ' a , F : Field > ReplayChannel < ' a , F > {
136126 /// Creates a new replay channel.
137- pub fn new ( layout : & ' a WitnessLayout < B128 > , events : Vec < B128 > ) -> Self {
127+ pub fn new ( layout : & ' a WitnessLayout < F > , events : Vec < F > ) -> Self {
138128 Self {
139129 witness_gen : Rc :: new ( RefCell :: new ( WitnessGenerator :: new ( layout) ) ) ,
140130 events : events. into_iter ( ) ,
141131 next_inout_id : 0 ,
142132 }
143133 }
144134
145- fn next_inout_elem ( & mut self , value : B128 ) -> WitnessElem < ' a > {
135+ fn next_inout_elem ( & mut self , value : F ) -> CircuitElem < WitnessGenerator < ' a , F > > {
146136 let wire = ConstraintWire :: inout ( self . next_inout_id ) ;
147137 self . next_inout_id += 1 ;
148138 let witness_wire = self . witness_gen . borrow_mut ( ) . write_inout ( wire, value) ;
149- WitnessElem :: Wire ( WitnessWire :: new ( & self . witness_gen , witness_wire) )
139+ CircuitElem :: Wire ( CircuitWire :: new ( & self . witness_gen , witness_wire) )
150140 }
151141
152- fn next_event ( & mut self ) -> B128 {
142+ fn next_event ( & mut self ) -> F {
153143 self . events
154144 . next ( )
155145 . unwrap_or_else ( || panic ! ( "replay exhausted: no more events" ) )
156146 }
157147
158148 /// Consumes the channel and builds the outer witness.
159- pub fn finish (
160- self ,
161- ) -> Result < Vec < B128 > , binius_spartan_frontend:: circuit_builder:: WitnessError > {
149+ pub fn finish ( self ) -> Result < Vec < F > , binius_spartan_frontend:: circuit_builder:: WitnessError > {
162150 Rc :: try_unwrap ( self . witness_gen )
163151 . expect ( "CircuitElem values should only hold Weak references" )
164152 . into_inner ( )
165153 . build ( )
166154 }
167155}
168156
169- impl < ' a > IPVerifierChannel < B128 > for ReplayChannel < ' a > {
170- type Elem = WitnessElem < ' a > ;
157+ impl < ' a , F : Field > IPVerifierChannel < F > for ReplayChannel < ' a , F > {
158+ type Elem = CircuitElem < WitnessGenerator < ' a , F > > ;
171159
172160 fn recv_one ( & mut self ) -> Result < Self :: Elem , binius_ip:: channel:: Error > {
173161 let val = self . next_event ( ) ;
@@ -179,7 +167,7 @@ impl<'a> IPVerifierChannel<B128> for ReplayChannel<'a> {
179167 }
180168
181169 fn recv_array < const N : usize > ( & mut self ) -> Result < [ Self :: Elem ; N ] , binius_ip:: channel:: Error > {
182- let mut result = [ ( ) ; N ] . map ( |_| WitnessElem :: Constant ( B128 :: ZERO ) ) ;
170+ let mut result = [ ( ) ; N ] . map ( |_| CircuitElem :: Constant ( F :: ZERO ) ) ;
183171 for elem in & mut result {
184172 * elem = self . recv_one ( ) ?;
185173 }
@@ -191,28 +179,28 @@ impl<'a> IPVerifierChannel<B128> for ReplayChannel<'a> {
191179 self . next_inout_elem ( val)
192180 }
193181
194- fn observe_one ( & mut self , _val : B128 ) -> Self :: Elem {
182+ fn observe_one ( & mut self , _val : F ) -> Self :: Elem {
195183 let val = self . next_event ( ) ;
196184 self . next_inout_elem ( val)
197185 }
198186
199- fn observe_many ( & mut self , vals : & [ B128 ] ) -> Vec < Self :: Elem > {
187+ fn observe_many ( & mut self , vals : & [ F ] ) -> Vec < Self :: Elem > {
200188 vals. iter ( ) . map ( |& val| self . observe_one ( val) ) . collect ( )
201189 }
202190
203191 fn assert_zero ( & mut self , val : Self :: Elem ) -> Result < ( ) , binius_ip:: channel:: Error > {
204192 match val {
205- WitnessElem :: Constant ( c) if c == B128 :: ZERO => Ok ( ( ) ) ,
206- WitnessElem :: Constant ( _) => Err ( binius_ip:: channel:: Error :: InvalidAssert ) ,
207- WitnessElem :: Wire ( w) => {
193+ CircuitElem :: Constant ( c) if c == F :: ZERO => Ok ( ( ) ) ,
194+ CircuitElem :: Constant ( _) => Err ( binius_ip:: channel:: Error :: InvalidAssert ) ,
195+ CircuitElem :: Wire ( w) => {
208196 self . witness_gen . borrow_mut ( ) . assert_zero ( w. wire ( ) ) ;
209197 Ok ( ( ) )
210198 }
211199 }
212200 }
213201}
214202
215- impl < ' a > IOPVerifierChannel < B128 > for ReplayChannel < ' a > {
203+ impl < ' a , F : Field > IOPVerifierChannel < F > for ReplayChannel < ' a , F > {
216204 type Oracle = ( ) ;
217205
218206 fn remaining_oracle_specs ( & self ) -> & [ OracleSpec ] {
0 commit comments