pub struct CircuitBuilder { /* private fields */ }Expand description
Circuit builder for constructing zero-knowledge proof circuits.
CircuitBuilder provides the primary interface for constructing circuits in the Binius64
proof system. The builder compiles imperative gate operations into a constraint system
suitable for zero-knowledge proof generation.
§Circuit Model
A circuit represents computation as a directed acyclic graph where 64-bit values flow
through gates via wires. Gates transform input wires to produce output wires.
Methods like band and iadd_32 add gates to the graph and return handles
to output wires.
During build, the gate graph compiles into AND and MUL constraints
that the proof system operates on directly.
§Wire Types
Wires are handles to 64-bit values that exist during proof generation. During circuit construction, wires represent value placeholders.
Constants - Values known at compile time. Zero constraint cost as both prover
and verifier know these values. Created with add_constant.
Public inputs/outputs - Values visible to both prover and verifier.
Form part of the proof statement (e.g., hash output in a preimage proof).
Created with add_inout.
Private witnesses - Values known only to the prover.
The circuit proves knowledge of these values without revealing them
(e.g., preimage in a hash proof). Created with add_witness.
Internal wires - Created automatically by gate operations. Represent intermediate computation values.
§MSB-Boolean Convention
Boolean values encode in the most significant bit (bit 63) of a 64-bit word. MSB = 1 represents true, MSB = 0 represents false. The lower 63 bits are “don’t care” values.
§Constraint Costs
AND constraints - Baseline unit of cost. Bitwise operations and comparisons generate 1-2 AND constraints.
MUL constraints - 64-bit multiplication costs ~3-4× more than AND constraints.
Committed values - Each public input/output and witness adds to proof size (~0.2× of an AND constraint).
Linear operations - XOR and shifts generate virtual linear constraints. During compilation these either:
- Fuse into adjacent non-linear gates (near-zero cost)
- Materialize as AND constraints
Gate fusion inlines compatible XOR expressions and shifts into existing AND gates. Incompatible operations (e.g., right shift into left shift) and heuristic limits prevent some fusions. XORs typically cost <0.1× of an AND constraint, shifts slightly more.
§Compilation
The builder uses reference-counted sharing internally. subcircuit returns
a builder referencing the same graph with hierarchical naming.
build triggers compilation:
- Validates the circuit structure
- Runs optimization passes (constant propagation, gate fusion)
- Generates the final constraint system
build consumes internal state and can only be called once per builder instance.
Implementations§
Source§impl CircuitBuilder
impl CircuitBuilder
Sourcepub fn build(&self) -> Circuit
pub fn build(&self) -> Circuit
Returns the circuit built by this builder.
Note that cloning the circuit builder only clones the reference and as such is treated as a shallow copy.
§Preconditions
Must be called only once.
Sourcepub fn subcircuit(&self, name: impl Into<String>) -> CircuitBuilder
pub fn subcircuit(&self, name: impl Into<String>) -> CircuitBuilder
Creates a reference to the same underlying circuit builder that is namespaced to the given name.
This is useful for creating subcircuits within a larger circuit.
Note that this is the same builder instance, but with a different namespace, and that means
calling Self::build on the returned builder is going to build the whole circuit.
Sourcepub fn force_commit(&self, wire: Wire)
pub fn force_commit(&self, wire: Wire)
Force commit the given wire.
This annotate the wire to be forcefully committed. This instructs optimization passes (ATOW only gate fusion) to forcibly materialize wire.
Sourcepub fn add_constant(&self, word: Word) -> Wire
pub fn add_constant(&self, word: Word) -> Wire
Sourcepub fn add_constant_64(&self, c: u64) -> Wire
pub fn add_constant_64(&self, c: u64) -> Wire
Creates a constant wire from a 64-bit unsigned integer.
This method adds a 64-bit constant value to the circuit. The constant is stored
as a Word and can be used in constraints and operations.
Constants are automatically deduplicated - multiple calls with the same value will return the same wire.
§Arguments
c- The 64-bit constant value to add to the circuit
§Returns
A Wire representing the constant value that can be used in circuit operations
Sourcepub fn add_constant_zx_8(&self, c: u8) -> Wire
pub fn add_constant_zx_8(&self, c: u8) -> Wire
Creates a constant wire from an 8-bit value, zero-extended to 64 bits.
This method takes an 8-bit unsigned integer (byte) and zero-extends it to a 64-bit value before adding it as a constant to the circuit. The resulting wire contains the byte value in the lower 8 bits and zeros in the upper 56 bits. This is commonly used for byte constants in circuits that process byte data.
§Arguments
c- The 8-bit constant value (0-255) to add to the circuit
Sourcepub fn add_inout(&self) -> Wire
pub fn add_inout(&self) -> Wire
Creates a public input/output wire.
Public wires form part of the proof statement and are visible to both prover and verifier. They are committed in the public section of the value vector alongside constants.
The wire must be manually assigned a value using WitnessFiller before circuit
evaluation.
Sourcepub fn add_witness(&self) -> Wire
pub fn add_witness(&self) -> Wire
Creates a private input wire.
Private wires contain secret values known only to the prover. They are placed in the private section of the value vector and are not revealed to the verifier.
The wire must be manually assigned a value using WitnessFiller before circuit
evaluation.
Sourcepub fn bxor_multi(&self, wires: &[Wire]) -> Wire
pub fn bxor_multi(&self, wires: &[Wire]) -> Wire
Multi-way bitwise XOR operation.
Takes a variable-length slice of wires and XORs them all together.
Returns z = i ^ j ^ k ^ …
§Cost
1 linear constraint.
Sourcepub fn fax(&self, x: Wire, y: Wire, w: Wire) -> Wire
pub fn fax(&self, x: Wire, y: Wire, w: Wire) -> Wire
Fused AND-XOR operation.
Computes (x & y) ^ w in a single gate.
Returns z = (x & y) ^ w
§Cost
1 AND constraint.
Sourcepub fn iadd_32(&self, a: Wire, b: Wire) -> Wire
pub fn iadd_32(&self, a: Wire, b: Wire) -> Wire
Parallel 32-bit integer addition.
Performs simultaneous independent 32-bit additions on the upper and lower halves, discarding the carry-out.
§Cost
1 AND constraint, 1 linear constraint.
Sourcepub fn iadd32_cin_cout(&self, a: Wire, b: Wire, cin: Wire) -> (Wire, Wire)
pub fn iadd32_cin_cout(&self, a: Wire, b: Wire, cin: Wire) -> (Wire, Wire)
Parallel 32-bit integer addition with carry-in and carry-out.
Performs simultaneous independent 32-bit additions on the upper and lower halves of the 64-bit word, with per-half carry-in and carry-out.
The carry-in for each half is taken from the MSB of that half in cin:
bit 31 for the lower half, bit 63 for the upper half. The carry-out
is a full carry word where bit 31 and bit 63 indicate the carry-out
of the lower and upper halves respectively.
§Cost
1 AND constraint, 1 linear constraint.
Sourcepub fn iadd(&self, a: Wire, b: Wire) -> (Wire, Wire)
pub fn iadd(&self, a: Wire, b: Wire) -> (Wire, Wire)
64-bit integer addition returning the sum and carry-out.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn iadd_cin_cout(&self, a: Wire, b: Wire, cin: Wire) -> (Wire, Wire)
pub fn iadd_cin_cout(&self, a: Wire, b: Wire, cin: Wire) -> (Wire, Wire)
64-bit integer addition with carry input and output.
Performs full 64-bit unsigned addition of two wires plus a carry input.
Returns (sum, carry_out) where:
sumis the 64-bit result andcarry_outis a 64-bit word where every bit position with a carry is set to 1.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn isub_bin_bout(&self, a: Wire, b: Wire, bin: Wire) -> (Wire, Wire)
pub fn isub_bin_bout(&self, a: Wire, b: Wire, bin: Wire) -> (Wire, Wire)
64-bit subtraction with borrow input and output.
Performs full 64-bit unsigned subtraction of two wires plus a borrow input.
Returns (diff, borrow_out) where:
diffis the 64-bit result andborrow_outis a 64-bit word where every bit position with a borrow is set to 1.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn shl(&self, a: Wire, n: u32) -> Wire
pub fn shl(&self, a: Wire, n: u32) -> Wire
Logical left shift.
Shifts a 64-bit wire left by n bits, filling with zeros from the right.
Returns a << n
§Cost
1 AND constraint.
Sourcepub fn shr(&self, a: Wire, n: u32) -> Wire
pub fn shr(&self, a: Wire, n: u32) -> Wire
Logical right shift.
Shifts a 64-bit wire right by n bits, filling with zeros from the left.
Returns a >> n
§Cost
1 AND constraint.
Sourcepub fn sar(&self, a: Wire, n: u32) -> Wire
pub fn sar(&self, a: Wire, n: u32) -> Wire
Arithmetic right shift.
Shifts a 64-bit wire right by n bits, filling with the MSB from the left.
Returns a SAR n
§Cost
1 AND constraint.
Sourcepub fn assert_eq(&self, name: impl Into<String>, x: Wire, y: Wire)
pub fn assert_eq(&self, name: impl Into<String>, x: Wire, y: Wire)
Equality assertion.
Asserts that two 64-bit wires are equal.
Takes wires x and y and enforces x == y. If the assertion fails, the circuit will report an error with the given name.
§Cost
1 AND constraint.
Sourcepub fn assert_eq_v<const N: usize>(
&self,
name: impl Into<String>,
x: [Wire; N],
y: [Wire; N],
)
pub fn assert_eq_v<const N: usize>( &self, name: impl Into<String>, x: [Wire; N], y: [Wire; N], )
Vector equality assertion.
Asserts that two arrays of 64-bit wires are equal element-wise.
Takes wire arrays x and y and enforces x[i] == y[i] for all i.
Each element assertion is named with the base name and index.
§Cost
N AND constraints (one per element).
Sourcepub fn assert_zero(&self, name: impl Into<String>, x: Wire)
pub fn assert_zero(&self, name: impl Into<String>, x: Wire)
Asserts that the given wire equals zero.
Enforces that x = 0 exactly. Every bit of the 64-bit value must be zero.
§Cost
1 AND constraint.
Sourcepub fn assert_non_zero(&self, name: impl Into<String>, x: Wire)
pub fn assert_non_zero(&self, name: impl Into<String>, x: Wire)
Asserts that the given wire is not zero.
Enforces that x ≠ 0. At least one bit must be non-zero.
§Cost
1 AND constraint.
Sourcepub fn assert_false(&self, name: impl Into<String>, x: Wire)
pub fn assert_false(&self, name: impl Into<String>, x: Wire)
Asserts that the given wire’s MSB (Most Significant Bit) is 0.
This treats the wire as an MSB-boolean where:
- MSB = 0 → false (assertion passes)
- MSB = 1 → true (assertion fails)
All bits except the MSB are ignored. This is commonly used with comparison results which return MSB-boolean values.
§Cost
1 AND constraint.
Sourcepub fn assert_true(&self, name: impl Into<String>, x: Wire)
pub fn assert_true(&self, name: impl Into<String>, x: Wire)
Asserts that the given wire’s MSB (Most Significant Bit) is 1.
This treats the wire as an MSB-boolean where:
- MSB = 1 → true (assertion passes)
- MSB = 0 → false (assertion fails)
All bits except the MSB are ignored. This is commonly used with comparison results which return MSB-boolean values.
§Cost
1 AND constraint.
Sourcepub fn imul(&self, a: Wire, b: Wire) -> (Wire, Wire)
pub fn imul(&self, a: Wire, b: Wire) -> (Wire, Wire)
64-bit × 64-bit → 128-bit unsigned multiplication.
Performs unsigned integer multiplication of two 64-bit values, producing a 128-bit result split into high and low 64-bit words.
Returns (hi, lo) where a * b = (hi << 64) | lo
§Cost
- 1 MUL constraint,
- 1 AND constraint (for security check).
Sourcepub fn smul(&self, a: Wire, b: Wire) -> (Wire, Wire)
pub fn smul(&self, a: Wire, b: Wire) -> (Wire, Wire)
64-bit × 64-bit → 128-bit signed multiplication.
Performs signed integer multiplication of two 64-bit values, producing a 128-bit result split into high and low 64-bit words. Correctly handles two’s complement signed integers including overflow cases.
Returns (hi, lo) where the signed multiplication result equals (hi << 64) | lo.
The high word is sign-extended based on the product’s sign.
§Cost
- 1 MUL constraint
- 7 AND constraints (2 for sign corrections, 4 for modular additions, 1 for low word equality).
Sourcepub fn assert_eq_cond(
&self,
name: impl Into<String>,
x: Wire,
y: Wire,
cond: Wire,
)
pub fn assert_eq_cond( &self, name: impl Into<String>, x: Wire, y: Wire, cond: Wire, )
Conditional equality assertion.
Asserts that two 64-bit wires are equal only when a condition is true (MSB = 1). When the condition is false (MSB = 0), no constraint is enforced.
§Cost
1 AND constraint.
Sourcepub fn icmp_ult(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_ult(&self, x: Wire, y: Wire) -> Wire
Unsigned less-than comparison.
Compares two 64-bit wires as unsigned integers.
Returns:
- a wire whose MSB-bool value is true if a < b
- a wire whose MSB-bool value is false if a ≥ b
the non-most-significant bits of the output wire are undefined.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn icmp_ule(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_ule(&self, x: Wire, y: Wire) -> Wire
Unsigned less-than-or-equal comparison.
Compares two 64-bit wires as unsigned integers.
Returns:
- a wire whose MSB-bool value is true if x <= y
- a wire whose MSB-bool value is false if x > y
the non-most-significant bits of the output wire are undefined.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn icmp_ugt(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_ugt(&self, x: Wire, y: Wire) -> Wire
Unsigned greater-than comparison.
Compares two 64-bit wires as unsigned integers.
Returns:
- a wire whose MSB-bool value is true if x > y
- a wire whose MSB-bool value is false if x <= y
the non-most-significant bits of the output wire are undefined.
§Cost
1 AND constraint.
Sourcepub fn icmp_uge(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_uge(&self, x: Wire, y: Wire) -> Wire
Unsigned greater-than-or-equal comparison.
Compares two 64-bit wires as unsigned integers.
Returns:
- a wire whose MSB-bool value is true if x >= y
- a wire whose MSB-bool value is false if x < y
the non-most-significant bits of the output wire are undefined.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn icmp_eq(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_eq(&self, x: Wire, y: Wire) -> Wire
Equality comparison.
Compares two 64-bit wires for equality.
Returns:
- a wire whose MSB-bool value is true if a == b
- a wire whose MSB-bool value is false if a != b
the non-most-significant bits of the output wire are undefined.
§Cost
1 AND constraint.
Sourcepub fn icmp_ne(&self, x: Wire, y: Wire) -> Wire
pub fn icmp_ne(&self, x: Wire, y: Wire) -> Wire
Inequality comparison.
Compares two 64-bit wires for inequality.
Returns:
- a wire whose MSB-bool value is true if a != b
- a wire whose MSB-bool value is false if a == b
the non-most-significant bits of the output wire are undefined.
§Cost
- 1 AND constraint,
- 1 linear constraint.
Sourcepub fn extract_byte(&self, word: Wire, j: u32) -> Wire
pub fn extract_byte(&self, word: Wire, j: u32) -> Wire
Sourcepub fn select(&self, cond: Wire, t: Wire, f: Wire) -> Wire
pub fn select(&self, cond: Wire, t: Wire, f: Wire) -> Wire
Select operation.
Returns t if cond is true (MSB-bit set), otherwise returns f.
§Cost
1 AND constraint.
Sourcepub fn call_hint<T: Hint>(
&self,
hint: T,
dimensions: &[usize],
inputs: &[Wire],
) -> Vec<Wire>
pub fn call_hint<T: Hint>( &self, hint: T, dimensions: &[usize], inputs: &[Wire], ) -> Vec<Wire>
Invoke a Hint and emit the corresponding gate.
Registers hint in the builder’s hint registry (idempotent, keyed by T::NAME),
allocates output wires according to hint.shape(dimensions), and emits a
generic hint gate. Returns the freshly allocated output wires.
dimensions is passed verbatim to Hint::shape and Hint::execute; it is the
hint’s parameterization (e.g., limb counts for a bignum hint).
§Panics
Panics if inputs.len() does not match the hint’s declared input arity.
Sourcepub fn biguint_divide_hint(
&self,
dividend: &[Wire],
divisor: &[Wire],
) -> (Vec<Wire>, Vec<Wire>)
pub fn biguint_divide_hint( &self, dividend: &[Wire], divisor: &[Wire], ) -> (Vec<Wire>, Vec<Wire>)
BigUint division.
Returns (quotient, remainder) of the division of dividend by divisor.
This is a hint - a deterministic computation that happens only on the prover side.
The result should be additionally constrained by using bignum circuits to check that
remainder + divisor * quotient == dividend.
Sourcepub fn biguint_mod_pow_hint(
&self,
base: &[Wire],
exp: &[Wire],
modulus: &[Wire],
) -> Vec<Wire>
pub fn biguint_mod_pow_hint( &self, base: &[Wire], exp: &[Wire], modulus: &[Wire], ) -> Vec<Wire>
Modular exponentiation.
Computes (base^exp) % modulus.
This is a hint - a deterministic computation that happens only on the prover side.
The result should be additionally constrained using bignum circuits.
Sourcepub fn mod_inverse_hint(
&self,
base: &[Wire],
modulus: &[Wire],
) -> (Vec<Wire>, Vec<Wire>)
pub fn mod_inverse_hint( &self, base: &[Wire], modulus: &[Wire], ) -> (Vec<Wire>, Vec<Wire>)
Modular inverse.
Computes the modular inverse of base modulo modulus.
Returns a pair (quotient, inverse) where both numbers are Bézout coefficients when
base and modulus are coprime. Both numbers are set to zero if gcd(base, modulus) > 1.
This is a hint - a deterministic computation that happens only on the prover side.
The result should be additionally constrained by using bignum circuits to check that
base * inverse = 1 + quotient * modulus.
Sourcepub fn secp256k1_endomorphism_split_hint(
&self,
k: &[Wire],
) -> (Wire, Wire, [Wire; 2], [Wire; 2])
pub fn secp256k1_endomorphism_split_hint( &self, k: &[Wire], ) -> (Wire, Wire, [Wire; 2], [Wire; 2])
Secp256k1 endomorphism split
The curve has an endomorphism λ (x, y) = (βx, y) where λ³=1 (mod n)
and β³=1 (mod p) (n being the scalar field modulus and p coordinate field one).
For a 256-bit scalar k it is possible to split it into k1 and k2 such that
k1 + λ k2 = k (mod n) and both k1 and k2 are no farther than 2^128 from zero.
The k scalar is represented by four 64-bit limbs in little endian order. The return value
is quadruple of (k1_neg, k2_neg, k1_abs, k2_abs) where k1_neg and k2_neg are
MSB-bools indicating whether k1_abs or k2_abs, respectively, should be negated.
k1_abs and k2_abs are at most 128 bits and are represented with two 64-bit limbs.
When k cannot be represented in this way (any valid scalar can, so it has to be modulus
or above), both k1_abs and k2_abs are assigned zero values.
This is a hint - a deterministic computation that happens only on the prover side.
The result should be additionally constrained by using bignum circuits to check that
k1 + λ k2 = k (mod n).
Trait Implementations§
Source§impl Clone for CircuitBuilder
impl Clone for CircuitBuilder
Source§fn clone(&self) -> CircuitBuilder
fn clone(&self) -> CircuitBuilder
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for CircuitBuilder
impl !RefUnwindSafe for CircuitBuilder
impl !Send for CircuitBuilder
impl !Sync for CircuitBuilder
impl Unpin for CircuitBuilder
impl UnsafeUnpin for CircuitBuilder
impl !UnwindSafe for CircuitBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more