---
# System prepended metadata

title: Feedback reviewer 2

---

# Feedback reviewer 2

While you make the differences clear (i.e. Circom is about explicitly constructing circuits by writing constraints, while other approaches compile programs into circuits), I think it's still not clear why this is better. The current comparison is pretty surface-level, and actually a lot of the differences are simply things that Circom doesn't support. For example, you say that in program-based approaches, "the compiler has to explore all paths through the program, unrolling all loops, considering all branches, while guarding all state modifications" - this is true, but the reason Circom doesn't have to do all this is because you simply don't support data-dependent control flow! So, while the revision makes the differences more clear, I think it still doesn't highlight the advantages of your approach.

I think it would be great if you could add one or more examples to show why this approach is better than the alternative - cases where a program-based approach might make it hard to express the statement, or might produce less efficient results.

I think just a handful of examples like this would sufficiently address my concern in this area. If any of the examples in Section 4 has this flavor, that's even better. I think IfZero might be a good candidate, since it requires producing additional witness values - a program-based approach is unlikely to offer control over this.

I'd still like to see some comparison against some other system on efficiency (as the other reviewers mentioned) but I personally think this is less important than the issue above.

# Answer

According to the reviewer's suggestion, we have included an example that shows the differences and advantages of `circom` in relation to program-based languages. In particular, in Section 3.2, we give an example of a circuit that outputs the inverse of a number written both in `ZoKrates` and `circom` languages. We think that this example shows why in some cases, the program-based approach might make it hard to express the statement. We think that the addition of this example in the new version of the manuscript is really useful, as the reviewer pointed.

We highlight the main differences of the two approaches and

------


I think it would be great if you could add one or more examples to show why this approach is better than the alternative - cases where a program-based approach might make it hard to express the statement, or might produce less efficient results.



The example program taken from the Zokrates official documentation is the following: 

```
def main(field x) -> field {
    return if x == 0 {
        0
    } else {
        1 / x 
    };
}
```

Here the programer wants to verify a program that returns 0 if a signal x is zero or the inverse of the signal if the signal is different of zero.
The problem is that, as the offical Zokrates documentation states, this leads to the execution failing since 1/x is computed even x := 0.

In circom, the previous program can be written as:

```
template Inverse() {
    signal input in;
    signal output out;

    signal inv;
    signal iszero;

    // compute the inverse of in, if in = 0, set inv = 0.    
    inv <-- in!=0 ? 1/in : 0;

    // if in = 0, iszero = 1, if in !=0, then iszero = 0.    
    iszero <== -in*inv + 1;
    in*iszero === 0;

    // if in = 0, out = 0, if in != 0, out = inv.    
    out <== (1 - iszero)*inv;
}

component main = Inverse();
```

When compiling the previous circom code, the circom compiler produces a witness calculator program that can be executed without issues. The differential factor between the circom code and the Zokrates code is that in circom the user can explicitly specify in the template how exactly the inv intermediate signal is computed, which avoids the division by zero of the Zokrates code.



# For paper

Next, we provide an example written in both `ZoKrates` and `circom` languages to show the differences between the two approaches. 

Let us look at the following program example taken from the ZoKrates official documentation \cite{}: 

```
def main(field x) -> field {
    return if x == 0 {
        0
    } else {
        1 / x 
    };
}
```
Here, the programer wants to verify a program that returns 0 if a given signal x is zero, and the inverse of the signal x if the signal is different of zero.
The problem is that, as the offical ZoKrates documentation states, this leads to the execution failing since 1/x is computed even when x := 0.

In circom, the previous program can be written as:

```
template Inverse() {
    signal input in;
    signal output out;

    signal inv;
    signal iszero;

    // compute the inverse of in, if in = 0, set inv = 0.    
    inv <-- in!=0 ? 1/in : 0;

    // Constraints for iszero
    // if in = 0, iszero = 1, if in !=0, then iszero = 0.    
    iszero <== -in*inv + 1;
    in*iszero === 0;

    // Last constraint to the output the inverse
    // if in = 0, out = 0, if in != 0, out = inv.    
    out <== (1 - iszero)*inv;
}

component main = Inverse();
```

The first two constraints in the previous circom code enforce that the signal named `iszero` is `1` if the input signal `in` is 0, and `0` otherwise (for a detailed explanation of these constraints for iszero see Section~\ref{}). The last constraint, sets the output `out` to 0 if the input `in` is 0, and with any other number, `out` is set as the inverse of the given number.
The differential factor between the circom code and the Zokrates code is that in circom the user can explicitly specify in the template how exactly the inv intermediate signal is computed, which avoids the division by zero problem of the Zokrates code and allows that our witness calculator runs without issues. 


## Is zero and inverse with ZoKrates

### Is zero

```
def main(field x) -> bool {
    return if x == 0 {
        true
    } else {
        false
    };
}
```

Wires: 5
Constraints: 3 (4 según ellos, he añadido su cuarta constraint a circom también)

```
wires: input one, input x, y (set by ZoKrates?), z (set by ZoKrates?), output out.
constraints: (0) one = 1;
             (1) x*z = y;
             (2) (one - y)*x = 0;
             (3) (one - y)*one = out;             
```

The logic is:
If `x = 0` -> `y = 0` -> `one^2 = out` -> `out = 1`.
If `x != 0` -> `one - y = 0` -> `out = 0`.

La `z` és l'invers, calculat per ZoKrates.

### Inverse

```
def main(field x) -> field {
    return if x == 0 {
        0
    } else {
        1 / x // executed even for x := 0, which leads to the execution failing
    };
}
```

## Is zero and inverse with circom

### Is zero

```
template IsZero() {
    signal input in;
    signal output out;

    signal inv;

    inv <-- in!=0 ? 1/in : 0;

    out <== -in*inv + 1;
    in*out === 0;
}
```
Wires: 4
Constraints: 2 (3 si contamos la del "one")

```
wires: input one, input in, inv ("set" by circom?), output out.
constraints: (0) one = 1;
             (1) one - in*inv = out;
             (2) in*out = 0;
```

### Inverse

```
template Inverse() {
    signal input in;
    signal output out;

    signal inv;
    signal iszero;

    // compute the inverse of in, if in = 0, set inv = 0.    
    inv <-- in!=0 ? 1/in : 0;

    // if in = 0, iszero = 1, if in !=0, then iszero = 0.    
    iszero <== -in*inv + 1;
    in*iszero === 0;

    // if in = 0, out = 0, if in != 0, out = inv.    
    out <== (1 - iszero)*inv;
}

component main = Inverse();
```


## Differences
1. no sabes cómo está implementado.
2. no tienes control sobre y, el equivalente en circom sería el inv, pero puede ser setteado por el usuario, en el caso de zokrates no sabes ni que esa señal forma parte del circuito.
3. no tienes opción a optimizar las constraints.
4. estás vendido al compilador.
5. por otro lado - la ventaja de circom noes solaemnte esta, sino que además puedes sacar más provecho de la divisón entre el cálculo y las constraints - un claro ejemplo (illustrative example) es la división entre dos field elements.

Els llenguatges a alt nivell venen abstracció, però no pot ser realment com hem vist en aquest cas perquè, si no saps el que passa: a vegades és ineficient, o mal funcionament.
- creen ineficiències
- el programador no es pot abstreure totalment del que passa per baix
- això no passa perquè: (1) per les ineficiències (2) per mal funcionaments