#3 Circom: Parametrizing circuits
Previously, we touched upon parametrizing circuits by introducing template parameters, loops and variables. This article explores parametrizing best practices and common pitfalls.
Known and unknown values
Before we get to parametrization, let's define what Circom considers "known" and "unknown" values:
Known values: values that are fixed and determined at compile time, such as constants and template parameters.
Unknown values: values that are not fixed at compile time; such as signals which can be assigned arbitrary values post-compilation.
Parametrizing and constraint generation
To enforce that constraint expressions cannot be non-quadratic, the compiler enforces restrictions on how unknown values (signals) are used.
The following example illustrates the downstream effects of using unknown and known variables:
since
n
is known → variabley
is knownsince
in
is unknown → variablez
in unknown
Whether a variable is known or unknown will be determined by its assignment. The known-ness of a variable will dictate how and where it can be used for parametrization.
We examine this in the following sections.
Arrays
Arrays (of signals or variables) must have a fixed known size at compile time.
cannot use signals to define array size
can use template parameters to define array size
Negative Example
Since the input signal, in
, is unknown, we cannot use it to define an array.
Compiler will return an error:
Error: The length of every array must known during the constraint generation phase
Positive Example
n
is a template parameter; can be used to define arrayon compilation the array is compiled as:
someArray[5]
Arrays and constraint generation
When using an array in a constraint, the array element (and therefore index) being accessed must be known at compile time.
index must be known → cannot use signals
can use template parameters as array index
Negative Example
someArray
consists of 3 elementsin2
is constrained against an element of the arraythat element is dependent on input signal
in1
since
in1
is unknown, the constraint cannot be well-defined
Compiler will return an error:
Error: Non-quadratic constraint was detected statically, using unknown index will cause the constraint to be non-quadratic
Using a signal as an array index allows different elements to be selected, leading to arbitrarily defined constraints. This enables exploits based on favourable values; but the compiler prevents such vulnerabilities.
Positive Example
in2
remains constrained against an element of the arrayhowever, here the array index passed is known, as the template parameter is used
circuit will compile
To the reader: It is understood that the array, someArray
, has not been initialized; this is a trivial example that only serves to illustrates the use of template parameters.
Loops and constraint generation
Constraints can only be generated in control flows (if-else, for-loops) with known conditions.
cannot use signals to define loop iterations
can use template parameters to define loop iterations
Let’s look at for-loops first.
For loops
Negative Example
loop is expected to iterate from
i = 0
toi = in1-1
but the value of input signal
in1
is unknown
Since the number of iterations is unknown at compile time, the number of constraints is unknown.
Compiler will return an error:
Error: There are constraints depending on the value of the condition and it can be unknown during the constraint generation phase
Positive Example
for loop has 5 iterations
5 constraints are created
On compilation, all constraints must be known and defined; and in this example there are.
If-else
If the condition is unknown, the entire block of code inside the control flow statement is considered unknown, and no constraints can be generated inside it.
Negative Example
if condition is based on signal
in1
the condition
in1 > 0
is unknown at compile time.
The compiler can't predict if an if
block will execute, making it uncertain which constraints to include; so it's not allowed to ensure the R1CS system remains static and known at compile time.
Positive Example
Conversely, when the condition is known at compile time, the compiler can generate constraints within the control flow statement.
In summary, constraints in control flow statements require known conditions at compile time. Unknown conditions render the entire code block indeterminate, preventing constraint generation within it. This ensures that all constraints are well-defined and predictable during circuit compilation.
Summary
Circom distinguishes between known values (constants and template parameters) and unknown values (signals).
Arrays must have a fixed, known size at compile time. Template parameters can be used to define array sizes, but signals cannot.
When using an array in a constraint, the array's accessed position must be known at compile time.
Constraints can only be generated in control flows (if-else, for-loops) with known conditions.
For loops must have a known number of iterations, typically defined by template parameters.
In if-else statements, the condition must be known at compile time for constraints to be generated within the block.
These rules ensure that all constraints are well-defined and predictable during circuit compilation, preventing potential vulnerabilities in the circuit design.
Last updated
Was this helpful?