Template Misuse Remediation
How to fix incorrect template instantiation patterns including parameter mismatches and unconstrained wiring.
Template Misuse Remediation
Overview
Related Detector: Template Misuse
Template misuse covers three patterns: parameter mismatches (e.g., LessThan(8) used on 256-bit values), unused template outputs, and unconstrained template inputs. Each pattern silently degrades security without causing compilation errors. The fix depends on the specific misuse pattern.
Recommended Fix
Parameter Mismatch (Before / After)
// Before: LessThan(8) only valid for values < 256
template UnsafePriceCheck() {
signal input price; // Could be up to 2^64
signal input maxPrice;
component lt = LessThan(8); // WRONG: 8 bits, max value 255
lt.in[0] <== price;
lt.in[1] <== maxPrice;
lt.out === 1;
}
// After: Match parameter to actual data size
template SafePriceCheck() {
signal input price;
signal input maxPrice;
component lt = LessThan(64); // CORRECT: 64 bits for price values
lt.in[0] <== price;
lt.in[1] <== maxPrice;
lt.out === 1;
}
Unused Template Output (Before / After)
// Before: Hash output is never used
template UnsafeVerifier() {
signal input a, b;
component hash = Poseidon(2);
hash.inputs[0] <== a;
hash.inputs[1] <== b;
// hash.out is never read -- wasted computation, possible logic error
}
// After: Use or remove the component
template SafeVerifier() {
signal input a, b;
signal output commitment;
component hash = Poseidon(2);
hash.inputs[0] <== a;
hash.inputs[1] <== b;
commitment <== hash.out; // Output is used
}
Unconstrained Template Input (Before / After)
// Before: Input wired via <--
component rangeCheck = Num2Bits(64);
rangeCheck.in <-- amount; // BROKEN: unconstrained wiring
// After: Input wired via <==
component rangeCheck = Num2Bits(64);
rangeCheck.in <== amount; // CORRECT: constrained wiring
Alternative Mitigations
1. Wrapper Templates with Parameter Validation
Create wrapper templates that document and enforce parameter requirements:
// Wrapper that makes the bit width requirement explicit
template SafeLessThan(nBits) {
signal input in[2];
signal output out;
// Range check inputs to nBits before comparison
component check0 = Num2Bits(nBits);
check0.in <== in[0];
component check1 = Num2Bits(nBits);
check1.in <== in[1];
component lt = LessThan(nBits);
lt.in[0] <== in[0];
lt.in[1] <== in[1];
out <== lt.out;
}
2. Assertion Comments for Parameter Choices
Document why specific parameters are chosen, so reviewers can verify correctness:
// Price values are at most 2^64 - 1 (validated by input range check above)
component lt = LessThan(64);
Common Mistakes
Using a small bit width for “efficiency”: Smaller bit widths produce fewer constraints, but using a bit width smaller than the actual data size produces incorrect results for large values. Correctness must take priority.
Assuming unused outputs are harmless: An unused component output may indicate a logic error — the developer may have intended to use it in a constraint. Review whether the component is necessary.
Wiring inputs with <-- “because the value is correct”: The value may be correct during honest witness generation, but the constraint is what prevents a malicious prover from substituting a different value.
References
- circomlib GitHub repository — reference implementations with parameter documentation
- Circom Documentation: Templates and Components