Template Misuse
Detects incorrect template instantiation patterns including unused outputs, parameter mismatches, and unconstrained component output reads.
Template Misuse
Overview
Remediation Guide: How to Fix Template Misuse
The template misuse detector groups three patterns that arise when a parent template instantiates a sub-component incorrectly: outputs of the sub-component that the parent never reads, components instantiated with the wrong number of template parameters, and component outputs that the parent reads with <-- instead of <==.
Each pattern reflects a different kind of mistake. The unused-output case usually means the parent forgot to anchor a value the sub-component is meant to deliver. The parameter-mismatch case is a structural error that may compile under permissive parsers but produces incorrect circuits. The unconstrained-read case is a soundness bug — the parent is using a value from the sub-component without enforcing that the value matches the sub-component’s logic.
Why This Is an Issue
Sub-component outputs are the only mechanism by which a parent template inherits guarantees from a sub-template. When the parent reads an output via <==, the R1CS gains a constraint connecting the two templates. When the parent reads via <--, no constraint is generated; the parent simply copies the prover’s witness value, and the sub-template’s internal constraints are not anchored to anything the parent verifies.
Unused outputs are dangerous when the sub-template’s purpose is to enforce a check (for example, a range check or a Merkle proof verifier) and the check produces a boolean output. If the parent ignores the boolean, the check has no effect on the parent’s proof.
Parameter mismatches typically point at a refactor where a sub-template’s signature changed but the call sites were not updated. The sub-template may then operate on default or undefined parameter values, producing a circuit whose semantics no longer match the developer’s intent.
How to Resolve
Connect every meaningful sub-component output via <==. Match the parameter count to the sub-template’s declaration. Use <== to read component outputs unless a deliberate witness-only path exists, in which case add a separate === constraint.
// Vulnerable: range check output is ignored, and a value is read via <--
template Spend() {
signal input amount;
signal output commitment;
component check = RangeCheck(64);
check.in <== amount;
// check.ok is never read — the range check has no effect
component hasher = Poseidon(1);
hasher.inputs[0] <== amount;
commitment <-- hasher.out; // unconstrained copy
}
// Fixed: range check output is enforced, hasher output is constrained
template Spend() {
signal input amount;
signal output commitment;
component check = RangeCheck(64);
check.in <== amount;
check.ok === 1; // enforce the check
component hasher = Poseidon(1);
hasher.inputs[0] <== amount;
commitment <== hasher.out; // constraining read
}
Sample Sigvex Output
{
"detector": "template-misuse",
"severity": "high",
"confidence": 0.80,
"title": "Unused component output `ok` in template `Spend`",
"template": "Spend",
"line": 7,
"description": "Component `check` of type `RangeCheck` produces output `ok`, but the parent template never references it. The range check has no effect on the parent's R1CS.",
"recommendation": "Add a constraint such as `check.ok === 1` or `<==` the output into a parent signal that participates in subsequent constraints."
}
Detection Methodology
For each component instantiation in a template, the detector resolves the referenced sub-template, enumerates its declared outputs, and checks whether each output is named in any assignment within the parent. Outputs with no references are reported. Independently, the detector counts the arguments supplied at instantiation against the sub-template’s parameter list and reports mismatches. Finally, it walks the parent’s <-- assignments looking for right-hand sides that reference a component output without a corresponding === constraint elsewhere in the template.
Limitations
- Sub-templates loaded from external libraries are only analyzed if the parser ingested their source. Components from binary-only libraries are skipped.
- Outputs accessed through array indexing (
comp.out[i]) are recognized when the index is a literal, but dynamic indices may produce false negatives. - Parameter-count checks do not validate parameter types or ranges, only arity.
Related Detectors
- Unconnected Component Inputs — sub-component inputs the parent never wires
- Unchecked Component — sub-components whose constraints are not propagated
- Cross-Template Constraint Gap — constraints split across template boundaries