Skip to main content

CircuitBuilder

Struct CircuitBuilder 

Source
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:

  1. Validates the circuit structure
  2. Runs optimization passes (constant propagation, gate fusion)
  3. Generates the final constraint system

build consumes internal state and can only be called once per builder instance.

Implementations§

Source§

impl CircuitBuilder

Source

pub fn new() -> Self

Create a new circuit builder with default options.

Source

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.

Source

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.

Source

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.

Source

pub fn add_constant(&self, word: Word) -> Wire

Creates a wire from a 64-bit word.

§Arguments
  • word - The word to add to the circuit.
§Returns

A Wire representing the constant value. The wire might be aliased because the constants are deduplicated.

§Cost

Constants have no constraint cost - they are “free” in the circuit.

Source

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

Source

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
Source

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.

Source

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.

Source

pub fn band(&self, x: Wire, y: Wire) -> Wire

Bitwise AND.

Returns z = x & y

§Cost

1 AND constraint.

Source

pub fn bxor(&self, a: Wire, b: Wire) -> Wire

Bitwise XOR.

Returns z = x ^ y

§Cost

1 linear constraint.

Source

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.

Source

pub fn bnot(&self, a: Wire) -> Wire

Bitwise Not

Returns z = ~x

§Cost

1 linear constraint.

Source

pub fn bor(&self, a: Wire, b: Wire) -> Wire

Bitwise OR.

Returns z = x | y

§Cost

1 AND constraint.

Source

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.

Source

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.

Source

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.

Source

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.
Source

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:

  • sum is the 64-bit result and
  • carry_out is a 64-bit word where every bit position with a carry is set to 1.
§Cost
  • 1 AND constraint,
  • 1 linear constraint.
Source

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:

  • diff is the 64-bit result and
  • borrow_out is a 64-bit word where every bit position with a borrow is set to 1.
§Cost
  • 1 AND constraint,
  • 1 linear constraint.
Source

pub fn rotl32(&self, x: Wire, n: u32) -> Wire

32-bit half-wise rotate left.

Rotates the upper and lower 32-bit halves left independently by n. Bits do not cross the 32-bit lane boundary.

Returns x ROTL32 n

§Panics

Panics if n ≥ 32.

§Cost

1 AND constraint (0 if n = 0).

Source

pub fn rotr32(&self, x: Wire, n: u32) -> Wire

32-bit half-wise rotate right.

Rotates the upper and lower 32-bit halves right independently by n. Bits do not cross the 32-bit lane boundary.

Returns x ROTR32 n

§Panics

Panics if n ≥ 32.

§Cost

1 AND constraint (0 if n = 0).

Source

pub fn rotl(&self, x: Wire, n: u32) -> Wire

64-bit rotate left.

Rotates a 64-bit value left by n positions. Bits shifted out on the left wrap around to the right.

Returns x rotated left by n

§Panics

Panics if n ≥ 64.

§Cost

1 AND constraint (0 if n = 0).

Source

pub fn rotr(&self, x: Wire, n: u32) -> Wire

64-bit rotate right.

Rotates a 64-bit value right by n positions. Bits shifted out on the right wrap around to the left.

Returns x rotated right by n

§Panics

Panics if n ≥ 64.

§Cost

1 AND constraint (0 if n = 0).

Source

pub fn srl32(&self, x: Wire, n: u32) -> Wire

32-bit half-wise logical right shift.

Shifts the upper and lower 32-bit halves right independently by n. Bits do not cross the 32-bit lane boundary.

Returns x SRL32 n

§Panics

Panics if n ≥ 32.

§Cost

1 AND constraint.

Source

pub fn sll32(&self, x: Wire, n: u32) -> Wire

32-bit half-wise logical left shift.

Shifts the upper and lower 32-bit halves left independently by n. Bits do not cross the 32-bit lane boundary.

Returns x SLL32 n.

§Panics

Panics if n ≥ 32.

§Cost

1 AND constraint.

Source

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.

Source

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.

Source

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.

Source

pub fn sra32(&self, a: Wire, n: u32) -> Wire

32-bit half-wise arithmetic right shift.

Shifts the upper and lower 32-bit halves right independently by n, sign-extending each half from its own bit 31.

Returns x SRA32 n.

§Panics

Panics if n ≥ 32.

§Cost

1 AND constraint.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).
Source

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).
Source

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.

Source

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.
Source

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.
Source

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.

Source

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.
Source

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.

Source

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.
Source

pub fn extract_byte(&self, word: Wire, j: u32) -> Wire

Byte extraction.

Extracts byte j from a 64-bit word (j=0 is least significant byte).

Returns the extracted byte (0-255) in the low 8 bits, with high 56 bits zero.

§Panics

Panics if j is greater than or equal to 8.

§Cost
  • 1 AND constraint,
  • 1 linear constraint.
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> CircuitBuilder

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for CircuitBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more