ctrlc03/Learning Circom

Created Fri, 28 Apr 2023 10:40:30 +0100
591 Words

Circom 101

In progress

Trying to learn Circom by going through the basic circuits (that come with circomlib).

Concepts

Signals

Signals in Circom are immutable (thus the opposite of a variable), and their content is unknown at compile time even if a constant is assigned to it. All input signals are private by default, and public signals can only be distinguished when defining the main component.

Let’s take as an example the following simple circuit:

pragma circom 2.0.x;

template Main() {
  signal input x1;
  signal input x2;
  signal input x3;
  signal input x4;

  signal y1;
  signal y2;

  signal output out;

  y1 <-- x1 + x2;
  y2 <-- y1 / x3;
  out <-- y2 - x4;

  y1 === x1 + x2;
  y1 === y2 * x3;
  out === y2 - x4;
}

component main { public [ x2 ] } = Main();

We can see that we declare x2 as a public input signal at very end of the circuit, when declaring the main component.

Output signals on the other end, are always public and cannot be made private.

Syntax: signal x;

Can use the keywords input, and output. When neither of these keywords are used, the signal is considered an intermediate circuit.

Variables

Variables are used to assist in computations where no constraint is needed.

Syntax: var x;

Assignments and constraints

There are different ways of assigning signals (the direction of the arrow depends on the position of the signal):

  • <-- - assign but no constraint is generated (dangerous)
  • <== - assign and generate a constraint
  • --> - assign but no constraint is generated (dangerous)
  • ==> - assign and generate a constraint

Templates

IsZero

This simple circuit allows to check whether a value is equal to zero or not.

pragma circom 2.0.0;

template IsZero() {
    // write a template that returns true if the input is zero
    signal input in;
    signal output out;

    // the inverse of the signal 
    signal inv; 
    // if the input is zero, then the inverse is zero
    // otherwise, the inverse is 1 / input
    inv <-- in != 0 ? 1 / in : 0;

    // out is -input * inverse of in + 1
    out <== -in * inv + 1;

    // constraint that out is opposite of in 
    in * out === 0;
}

Bits2Num

Template to covert an array of bits to its decimal representation:

template Bits2Num(n) {
    // array of bits to convert
    signal input in[n];
    // signal to hold the result
    signal output out;
    // tmp variable to store the result
    var lc1=0;

    // the exponent
    var e2 = 1;
    // loop for however many bits we have
    for (var i = 0; i<n; i++) {
        // add to the tmp var the curent bit * exponent
        lc1 += in[i] * e2;
        // increase the exponent
        e2 = e2 + e2;
    }

    // assign and constrain the result to the output
    lc1 ==> out;
}

### Mux1


template MultiMux1(n) {
    // multi dimensional array of n and 2 elements
    signal input c[n][2];  // Constants
    signal input s;   // Selector
    // output 
    signal output out[n];

    // loop through each element
    for (var i=0; i<n; i++) {
        // 2nd - 1st * selector + 1st
        out[i] <== (c[i][1] - c[i][0])*s + c[i][0];
    }
}

template Mux1() {
    // tmp var (could be directly added inside the loop)
    var i;
    signal input c[2];  // Constants
    signal input s;   // Selector
    signal output out;

    component mux = MultiMux1(1);

    for (i=0; i<2; i++) {
        mux.c[0][i] <== c[i];
    }

    s ==> mux.s;

    mux.out[0] ==> out;
}