Unconstrained Output
Detects circuit output signals that lack any constraining assignment or explicit constraint, allowing the prover to set outputs to arbitrary values.
Unconstrained Output
Overview
Remediation Guide: How to Fix Unconstrained Outputs
The unconstrained output detector flags output signals that are not bound by any constraint. An output signal represents a value the circuit makes available to the verifier or to a parent circuit. If the output has no constraining assignment (<==) and no explicit constraint (===), the prover controls its value entirely.
This detector differs from the under-constrained signal detector. While that detector focuses on any signal assigned via <-- without a matching ===, this detector catches two distinct cases: outputs assigned via <-- only (no constraint), and outputs with no assignment at all (completely unbound).
Why This Is an Issue
Output signals are the circuit’s externally visible results. In recursive proof compositions and on-chain verification, the verifier trusts that output values are determined by the circuit’s logic and constrained inputs. An unconstrained output breaks this assumption: the prover can claim any result.
For example, a circuit that computes a Merkle root but leaves the root output unconstrained allows the prover to “prove” membership in any tree for any leaf. The proof is valid — the verifier accepts it — but the claimed root bears no relation to the actual computation.
How to Resolve
Ensure every output signal is assigned via <== or has a separate === constraint binding it.
// Before: Vulnerable -- output assigned via <-- only
signal input a;
signal output result;
result <-- a * a; // no constraint generated
// After: Fixed -- constraining assignment
signal input a;
signal output result;
result <== a * a; // constraint: result == a * a
When the output computation requires <-- (non-quadratic expression), add ===:
signal input a;
signal output result;
result <-- a / 2;
result * 2 === a; // explicit constraint
Examples
Vulnerable Code
template BrokenHash() {
signal input preimage;
signal output digest;
// Output declared but never assigned or constrained
// The prover can claim any digest value
}
template UnsafeSquare() {
signal input x;
signal output y;
y <-- x * x; // unconstrained assignment -- no constraint on y
}
Fixed Code
template SafeHash() {
signal input preimage;
signal output digest;
component hasher = Poseidon(1);
hasher.inputs[0] <== preimage;
digest <== hasher.out; // constrained via <==
}
template SafeSquare() {
signal input x;
signal output y;
y <== x * x; // constraining assignment
}
Sample Sigvex Output
CRITICAL unconstrained-output
Output signal `digest` in template `BrokenHash` has no constraining assignment
(`<==`) and no explicit constraint (`===`). The output value is completely
unconstrained.
Template: BrokenHash
Signal: digest
Confidence: 0.95
Recommendation: Add a constraining assignment (`<==`) or explicit constraint
(`===`) for this output signal to ensure circuit soundness.
The confidence is dynamic: 0.95 when the output has no assignment at all, 0.90 when the output is assigned via <-- without a constraint. The lower confidence for the <-- case accounts for the possibility that the constraint exists in a form the detector does not recognize.
Detection Methodology
For each template, the detector builds a set of constrained signals by collecting every signal name that appears in any <== or === expression (on either side of the operator). It then checks each declared output signal against this set. Outputs not in the constrained set are flagged.
The detector distinguishes between two cases:
- No assignment: the output is never assigned at all (higher confidence, 0.95).
- Unconstrained assignment only: the output appears on the LHS of a
<--but never in a constraint (confidence 0.90).
Signal names on the RHS of constraining expressions are also collected. If an output appears on the RHS of another signal’s <== (e.g., other <== output + 1), the output is considered constrained because the R1CS equation references it.
Limitations
- Does not follow component output wiring across template boundaries. If an output is wired to a sub-component’s constrained output, the detector may still flag it if the wiring uses
<--. - Confidence values are heuristic. A
<--assignment with a constraint in an unrecognized form (e.g., inside a component) may produce a false positive at 0.90 confidence.
Related Detectors
- under-constrained-signal — general under-constrained signal detection (not output-specific)
- unconstrained-public-input — public inputs without constraints
- unconnected-component-inputs — component inputs not wired to parent signals