Skip to main content
Sigvex

Unchecked Component Remediation

How to fix component instances with disconnected inputs or outputs that do not contribute to circuit security.

Unchecked Component Remediation

Overview

Related Detector: Unchecked Component

An unchecked component is a template instance whose inputs or outputs are not properly wired to the rest of the circuit. This includes outputs never read, outputs wired via <-- instead of <==, or inputs left unconnected. The component’s internal constraints exist in the R1CS but are disconnected from the proof’s security properties. The fix is to ensure all component I/O uses constrained wiring.

Before (Vulnerable – Output Wired via <--)

template UnsafeVerifier() {
    signal input a, b;
    signal output commitment;

    component hash = Poseidon(2);
    hash.inputs[0] <== a;
    hash.inputs[1] <== b;
    commitment <-- hash.out;  // BROKEN: unconstrained wiring
}

After (Fixed – Constrained Output Wiring)

template SafeVerifier() {
    signal input a, b;
    signal output commitment;

    component hash = Poseidon(2);
    hash.inputs[0] <== a;
    hash.inputs[1] <== b;
    commitment <== hash.out;  // CORRECT: constrained wiring
}

Before (Vulnerable – Output Never Read)

template UnsafeTransfer() {
    signal input amount;

    // Range check instantiated but output ignored
    component rangeCheck = Num2Bits(64);
    rangeCheck.in <== amount;
    // rangeCheck.out is never read -- but this is actually OK for Num2Bits
    // because the constraint is on the input decomposition.
    // However, for components where the output IS the security property:

    component lt = LessThan(64);
    lt.in[0] <== amount;
    lt.in[1] <== 1000;
    // lt.out is never checked! The comparison happens but is not enforced.
}

After (Fixed – Output Consumed in Constraint)

template SafeTransfer() {
    signal input amount;

    component rangeCheck = Num2Bits(64);
    rangeCheck.in <== amount;

    component lt = LessThan(64);
    lt.in[0] <== amount;
    lt.in[1] <== 1000;
    lt.out === 1;  // CORRECT: comparison result is enforced
}

Alternative Mitigations

1. Inline the Component’s Logic

If you only need the constraint and not the output signal, some components (like Num2Bits) provide the constraint through their internal structure. Verify that the component’s security property comes from its internal constraints, not from consuming its output.

2. Assert Output in Wrapper Template

Create a wrapper that enforces output consumption:

template AssertLessThan(n) {
    signal input in[2];
    component lt = LessThan(n);
    lt.in[0] <== in[0];
    lt.in[1] <== in[1];
    lt.out === 1;  // Always enforced
}

Common Mistakes

Assuming instantiation alone provides security: Creating a component instance adds its constraints to the R1CS, but those constraints only protect signals that are connected to the component via <==. Disconnected components are dead code.

Checking the wrong output: Some components have multiple outputs. Constraining one output but ignoring another may leave part of the security property unenforced.

Confusing Num2Bits with LessThan: Num2Bits(n) constrains its input to [0, 2^n) through its internal bit decomposition – the output bits are a byproduct. LessThan(n) produces a boolean output that must be explicitly checked. Different components have different constraint models.

References