2.1. Circuit structure

Templates

The mechanism to create generic circuits in circom is through the use of templates. The instantiation of a template is a new circuit object, which can be used to compose other circuits, so as part of larger circuits.

Templates are typically parametric on some values that must be instantiated when the template is used. Since templates define circuits by instantiation, they have their own input and output signals.

template tempid ( param_1 , ... , param_n ){
signal input a;
signal outpub b;
...
}

Currently, templates can not include local functions or template definitions.

The instantiation of a template is made using the keyword component and by providing the necessary parameters.

component c = tempid(v1,...,vn);

The values of the parameters should be known constants at compile time.

Compiler messages

The compiler may generate messages regarding the usage of the signals defined in the template that will be part of the component. More specifically:

  1. If a signal is not used in any constraint, an error message will be generated. Moreover, if such signal is an input signal s , then the compiler would suggest adding a constraint of the form x * 0 === 0.

  2. If an intermediary signal is used only in one constraint, a hint message will be generated.

  3. If there is no output signal a warning message will be generated.

Read more about the different compiler messages here.

Components

In circom, circuits are called components. These are created by instantiation of a template, which is parametrized in the general description of the circuit. The instantiation provides actual values to the parameters.

The main component

In order to start the execution, an initial component has to be given. By default, the name of this component is main . This component needs to be instantiated with some template.

The main component is a special initial component needed to create a circuit and it defines the global input and output signals of a circuit. For this reason, compared to the other components, it has a special attribute: the list of public input signals. The syntax of the creation of this component is

component main {public [signal_list]} = tempid(v1,...,vn);

where {public [signal_list]} is optional. Any input signal of the template that is not included in the list is considered private. You can read more about public and private signals here.

Component instantiation

The component instantiation will not be triggered until all its input signals are assigned to concrete values. Therefore, the instantiation might be delayed and hence, the component creation instruction does not imply the execution of the component object, but the creation of the instantiation process that will be completed when all the inputs will be set.

The output signals of a component can only be used when all inputs are set, otherwise a compiler error is generated. For instance, the following piece of code would result in an error:

template Internal() {
signal input in[2];
signal output out;
out <== in[0]*in[1];
}
template Main() {
signal input in[2];
signal output out;
component c = Internal ();
c.in[0] <== in[0];
c.out ==> out; // c.in[1] is not assigned yet
c.in[1] <== in[1]; // this line should be placed before calling c.out
}
component main = Main();

Properties of components

  • A component defines an arithmetic circuit and, as such, it receives some input signals and it produces a set of output signals and some intermediate signals.

  • Components can also produce a set of constraints.

  • Components, like signals, are immutable. A component can be declared first and initialized in a second step. If there are several initialization instructions (in different execution paths), then they all need to be instantiations of the same template (maybe with different values for the parameters).

  • In order to access the input or output signals of a component, we will use dot notation. No other signals are visible outside the component. See the following example:

c.a <== y*z-1;
var x;
x = c.b;
  • We can define arrays of components following the same restrictions on the size given before. Moreover, initialization in the definition in arrays of components is not allowed, and instantiation can only be made component by component, accessing to the positions of the array. All components in the array have to be instances of the same template.

template MultiAND(n) {
...
component ands[2];
ands[0] = MultiAND(n1);
ands[1] = MultiAND(n2);
...
}
  • Note also that, as shown in the example above, template definitions can be recursive.

Include

Templates, like other code, can be found in other files like in libraries (see circomlib). In order to use code from other files we have to include them in our program by using the keyword include with the corresponding name of the file. The default extension of the file is .circom.

include "../basics/addition.circom";
template Multiplication(n) {
signal input a;
signal input b;
component add = binary_add();
add.in[0] <== a;
add.in[1] <== b;
sum <== sum.out;
...
}

Functions

In circom, functions define generic abstract pieces of code that can perform some computations to obtain a value or an expression to be returned.

function func ( param_1, ... , param_n ) {
.....
return x;
}
  • Functions can compute numeric values, arrays of values or expressions, but they can not generate constraints. If you need to generate constraints, you should use templates instead.

  • Functions can be recursive.

  • As usual, there can be many return statements, but every execution trace must end in a return statement. The execution of the return statement returns the control to the caller of the function. All paths in the function should end with a return statement. If not, a compile error will be produced.