Skip to content

Commit 599c721

Browse files
committed
[spartan-wrapper] Generify over F: Field instead of hardcoded B128
1 parent a397b5e commit 599c721

File tree

3 files changed

+103
-115
lines changed

3 files changed

+103
-115
lines changed

crates/spartan-wrapper/src/channel.rs

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
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
55
use std::{cell::RefCell, rc::Rc};
66

7-
use binius_field::{BinaryField128bGhash as B128, Field};
7+
use binius_field::Field;
88
use binius_iop::channel::{IOPVerifierChannel, OracleLinearRelation, OracleSpec};
99
use binius_ip::channel::IPVerifierChannel;
1010
use binius_spartan_frontend::{
@@ -14,38 +14,45 @@ use binius_spartan_frontend::{
1414

1515
use 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

Comments
 (0)