iden3 / circom

zkSnark circuit compiler
GNU General Public License v3.0
1.25k stars 232 forks source link

[Question] Pass the array into and retrieve the array from a function #8

Closed a2468834 closed 2 years ago

a2468834 commented 2 years ago

Hello everyone~ 😀

Recently, I'm writing circom version of my python functions. (To make the zk proof of python functions' output)

If I have a python function, I can easily pass the array into and retrieve the array from that function.

def myFunc(in_array):
    # Some calculations
    return out_array

if __name__ == "__main__":
    in_array = [1, 2, 3];
    out_array = myFunc(in_array);

However, according to docs, I cannot find examples or somewhere else that talking about passing array into a function. Currently, I guess that it might be something like this:

function myFunc(in_array) {
    // Some calculations
    return out_array;
}

template mainFunc() {
    signal private input in_array; // A fixed length array
    signal private input b;
    signal output out;

    var out_array = myFunc(in_array);
}

component main = mainFunc();

Thanks for anyone willing to give me the answer or some clues about how I could develop in this situation. 🙏❤

jbaylina commented 2 years ago

functions are only for calculus of things that do not involve signals. In your case you should create a component using template instead of function.

In that case, you will not have any problem. (You need to define the size of the array).

a2468834 commented 2 years ago

Okay, I see.🙏 Therefore, if I use template for implementation. Would It be something like this?

template myFunc(length) {
    signal private input in_array[length];
    signal output out_array[length];

    var result[length];

    // Some calculations

    out_array[length] <-- result[length];
}

template mainFunc(length) {
    signal private input in_array[length];
    signal private input b;
    signal output out;

    component c = myFunc(length);

    // Pass input array
    for(var i = 0; i < length; i++) {
        c.in_array[i] <== in_array[i];
    }

    // Retrieve output array
    var out_array[length];
    for(var i = 0; i < length; i++) {
        out_array[i] = c.out_array[i];
    }
}

component main = mainFunc(A_NUMBER);
alrubio commented 2 years ago

Some comments: private is no longer used in circom 2.0 (https://docs.circom.io/circom-language/signals/)

What do you want to do with out_array[length] <-- result[length]; ? As it is written, it is an assignment of one position that is out of range. If you were willing to do an assignment of the full array of var "result" into the array of signals "out_array", this is not allowed. An array of signals should be assigned one by one. And recall that if you use <-- no constraint is generated.

out_array[i] = c.out_array[i]; should be out_array[i] <== c.out_array[i];

a2468834 commented 2 years ago

(1)

If you were willing to do an assignment of the full array of var "result" into the array of signals "out_array", this is not allowed.

Because I will apply some arithmetic operation onto in_array and get them back by storing values in the out_array, how could I write codes to achieve this?

// My comprehension so far
template myFunc(length) {
    signal private input in_array[length];
    signal inter temp[length];
    signal output out_array[length];

    // Do something and store temporary results in temp[length]

    for(var i = 0; i < length; i++) {
        out_array[i] <== temp[i];
    }   
}

(2) It's my bad that I didn't know "<==" can be use in the assignment of a var, so I used "=" to treat out_array[i] = c.out_array[i] Precisely speaking, is it valid that write a line of code: var <== signal

alrubio commented 2 years ago

(1) Now it is fine, but you have to remove the "private" and "inter". Moreover , depending on what you do temp could also be an array of var, as you had before. (2) Sorry, you are right. I was confused with the out_array of the above template where it was an array of signals. Since here it is an array of vars out_array[i] = c.out_array[i]; is ok The weird thing in the second template is that you are not defining the value of the output signal.

a2468834 commented 2 years ago

Wow, thanks for all of you are so accommodating and willing to give me a lot of helpful comments.👍❤ Here are some conclusions (no question anymore). I hope this could help other Circom newcomers.


(1) A very important rule!

An array of signals should be assigned one by one.

(2) Functions vs templates

Functions are only for calculating of things that do not involve signals. In your case, you should create a component using template instead of function.

(3) Some combinations and their validities about signal, var, <==, and =.

signal a;
signal b;

var var_a;
var var_b;

a <== 123;       // Valid
a <== b;         // Valid
a <== var_a;     // Valid

a = 123;         // Invalid, please use "<=="
a = b;           // Invalid, please use "<=="
a = var_a;       // Invalid, please use "<=="

var_a = 123;     // Valid
var_a = var_b;   // Valid
var_a = a;       // Valid

var_a <== 123;   // Invalid, please use "="
var_a <== a;     // Invalid, please use "="
var_a <== var_b; // Invalid, please use "="

(4) The revised pseudo-code which performs passing and retrieving array.

template foo(length) {
    signal input in_array_foo[length];
    signal temp[length]; // Store temporary results and can be assigned only once
    signal output out_array_foo[length];

    // Do something

    for(var i = 0; i<length; i++) {
        out_array_foo[i] <== temp[i];
    }
}

template mainFunc(length) {
    signal input in_array[length];
    signal output out_array[length];

    component bar = foo(length);

    // Pass input array
    for(var i = 0; i < length; i++) {
        bar.in_array_foo[i] <== in_array[i];
    }

    // Retrieve output array
    for(var i = 0; i < length; i++) {
        out_array[i] <== bar.out_array_foo[i];
    }
}

component main = mainFunc(NUMBER_DEFINED_STATICLY);