Cross-Template Constraint Gap Remediation
How to fix broken constraint chains between template instances by using constrained wiring.
Cross-Template Constraint Gap Remediation
Overview
Related Detector: Cross-Template Constraint Gap
A cross-template constraint gap occurs when signals passed between template instances use unconstrained assignment (<--) instead of constrained wiring (<==). Even if each template is individually sound, the prover can inject arbitrary values at template boundaries, breaking the overall circuit security.
Recommended Fix
Before (Vulnerable)
template Main() {
signal input data;
signal output result;
component hash = Poseidon(1);
hash.inputs[0] <== data;
component verify = Verifier();
verify.hashValue <-- hash.out; // BROKEN: unconstrained wiring
result <== verify.result;
}
After (Fixed — Constrained Wiring)
Replace all <-- wiring between components with <==:
template Main() {
signal input data;
signal output result;
component hash = Poseidon(1);
hash.inputs[0] <== data;
component verify = Verifier();
verify.hashValue <== hash.out; // CORRECT: constraint chain intact
result <== verify.result;
}
After (Fixed — Explicit Intermediate with Constraint)
When you need an intermediate signal between components, constrain it on both sides:
template Main() {
signal input data;
signal output result;
component hash = Poseidon(1);
hash.inputs[0] <== data;
signal intermediate;
intermediate <== hash.out; // Constrained from hash output
component verify = Verifier();
verify.hashValue <== intermediate; // Constrained into verifier
result <== verify.result;
}
Alternative Mitigations
1. Audit All Component Wiring Systematically
Review every component instantiation and verify that all inputs and outputs use <==. A simple search for <-- adjacent to component signal access (e.g., comp.signal <--) identifies potential gaps.
2. Wrapper Templates for Constraint Enforcement
Create wrapper templates that enforce constrained wiring as part of their interface:
template SafeHash(nInputs) {
signal input in[nInputs];
signal output out;
component h = Poseidon(nInputs);
for (var i = 0; i < nInputs; i++) {
h.inputs[i] <== in[i];
}
out <== h.out;
}
Common Mistakes
Mixing <-- and <== in multi-component chains: If template A feeds template B feeds template C, a single <-- anywhere in the chain breaks the entire constraint propagation. Every link must use <==.
Assuming component-internal constraints are sufficient: A component’s internal constraints verify relationships between its own signals. They do not constrain the wiring between the component and the parent template. The wiring itself must be constrained.
Using <-- for “performance” reasons: There is no performance benefit to using <-- over <== for component wiring. The <== operator generates both the assignment and the constraint, which is always necessary for inter-template connections.
References
- Circom Documentation: Templates and Components
- circomlib GitHub repository — reference implementations with proper component wiring