├── .eslintrc.js ├── .gitignore ├── COPYING ├── Project.sublime-project ├── README.md ├── TUTORIAL.md ├── cli.js ├── doc ├── lc_example.monopic ├── r1cs_bin_format.md └── wasmmodule.c ├── index.js ├── package-lock.json ├── package.json ├── parser ├── jaz.jison └── jaz.js ├── ports ├── c │ ├── builder.js │ └── tester.js └── wasm │ ├── build_runtime.js │ ├── builder.js │ ├── errs.js │ └── tester.js ├── src ├── bigarray.js ├── build.js ├── buildsyms.js ├── compiler.js ├── construction_phase.js ├── ctx.js ├── gencode.js ├── iterateast.js ├── lcalgebra.js ├── r1csfile.js ├── streamfromarray_bin.js ├── streamfromarray_txt.js └── utils.js ├── test ├── basiccases.js ├── basiccases.json └── circuits │ ├── add.circom │ ├── addconst1.circom │ ├── arrays.circom │ ├── assignsignal.circom │ ├── componentarray.circom │ ├── componentarray2.circom │ ├── compute.circom │ ├── condternary.circom │ ├── constantcircuit.circom │ ├── constantinternalcircuit.circom │ ├── constants1.circom │ ├── dec.circom │ ├── declareandistantiate.circom │ ├── fnarray.circom │ ├── forrolled.circom │ ├── forunrolled.circom │ ├── forvariables.circom │ ├── function1.circom │ ├── function2.circom │ ├── ifrolled.circom │ ├── ifunrolled.circom │ ├── in.bin │ ├── in.json │ ├── inc.circom │ ├── include.circom │ ├── included.circom │ ├── inout.circom │ ├── mixvarsignal.circom │ ├── modify_left.circom │ ├── ops.circom │ ├── ops2.circom │ ├── ops3.circom │ ├── opsbit.circom │ ├── opscmp.circom │ ├── opslog.circom │ ├── poma.circom │ ├── poma2.circom │ ├── undefinedif.circom │ ├── whilerolled.circom │ └── whileunrolled.circom └── utils └── mergesymbols.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "extends": "eslint:recommended", 11 | "rules": { 12 | "indent": [ 13 | "error", 14 | 4 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "double" 23 | ], 24 | "semi": [ 25 | "error", 26 | "always" 27 | ] 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | tmp 64 | 65 | .DS_Store 66 | 67 | # Workspace files are user-specific 68 | *.sublime-workspace 69 | 70 | -------------------------------------------------------------------------------- /Project.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".", 5 | } 6 | ], 7 | "settings": { 8 | "SublimeAnarchyDebug": { 9 | "debug": { 10 | "executable": "${project_path}/test/circuits/add", 11 | "params": [ 12 | "addin.json", 13 | "out.bin", 14 | ], 15 | "path": [ 16 | ], 17 | "environment": [ 18 | ], 19 | "working_dir": "${project_path}" 20 | } 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠ Important deprecation note 2 | 3 | This `circom` compiler is written in Javascript and it will be frozen. For new projects and any further development please use [new circom written in Rust](https://github.com/iden3/circom). 4 | 5 | # Circom 6 | 7 | Circom is a language designed to write arithmetic circuits that can be used in zero-knowledge proofs. 8 | 9 | In particular, it is designed to work in [zksnarks JavaScript library](https://github.com/iden3/zksnark). 10 | 11 | ## Usage 12 | 13 | ### Circom Docs 14 | 15 | You can read the details of circom in [the documentation webpage](https://docs.circom.io/). 16 | 17 | ### Tutorial 18 | 19 | A good starting point [is this tutorial](https://docs.circom.io/getting-started/installation/) 20 | 21 | Also this [video](https://www.youtube.com/watch?v=-9TJa1hVsKA) is a good starting point. 22 | 23 | ### First circuit 24 | 25 | Creation of a circuit. This is an example of a NAND door: 26 | 27 | ``` 28 | template NAND() { 29 | signal private input a; 30 | signal input b; 31 | signal output out; 32 | 33 | out <== 1 - a*b; 34 | a*(a-1) === 0; 35 | b*(b-1) === 0; 36 | } 37 | 38 | component main = NAND(); 39 | ``` 40 | 41 | The language uses mainly JavaScript/C syntax together with 5 extra operators to define the following constraints: 42 | 43 | `<==` , `==>` : These two operators are used to connect signals and at the same time imply a constraint. 44 | 45 | As it is shown in the above example, a value is assigned to `out` and a constraint is also generated. The assigned value must be of the form a*b+c where a,b and c are linear combinations of the signals. 46 | 47 | `<--` , `-->` : These operators assign values to signals but do not generate any constraints. This allows to assign to a signal any value involving strange operations like shifts, divisions, modulo operations, etc. In general, these operators are used together with a `===` operator in order to force the constraint. 48 | 49 | `===` : This operator defines a constraint. The constraint must be simplificable to a constraint of the form `a*b+c=0`, where `a`, `b` and `c` are linear combinations of the signals. 50 | 51 | In the above example, both inputs are forced to be binary by adding the constraints `a*(a-1)===0` and `b*(b-1) === 0`. 52 | 53 | ### Compilation the circuit 54 | 55 | First of all, the compiler must be installed by typing: 56 | 57 | ``` 58 | npm install -g circom 59 | ```` 60 | 61 | The circuit is compiled with the following command: 62 | 63 | ``` 64 | circom mycircuit.circom -o mycircuit.json 65 | ``` 66 | 67 | The resulting output ( `mycircuit.json` ) can be used in the [zksnarks JavaScript library](https://github.com/iden3/zksnark). 68 | 69 | In this library one can do the trusted setup, create the proofs and verify them. 70 | 71 | ### Number to binary 72 | 73 | In many situations, one has to convert an input to its binary representation. Therefore, the circuits can be written this way: 74 | 75 | ``` 76 | template Num2Bits(n) { 77 | signal input in; 78 | signal output out[n]; 79 | var lc1=0; 80 | 81 | for (var i = 0; i> i) & 1; 83 | out[i] * (out[i] -1 ) === 0; 84 | lc1 += out[i] * 2**i; 85 | } 86 | 87 | lc1 === in; 88 | } 89 | 90 | component main = Num2Bits(8) 91 | ``` 92 | 93 | First of all, note that templates can have parameters. This allows to create libraries with templates that generate circuits in parametric ways. In this case, the circuit has an output of 8 signals, but one can easily instantiate any circuit with any number of outputs. 94 | 95 | The inputs and outputs are defined as arrays. The programm allows multidimensional arrays for signals and variables. 96 | 97 | Then, the values are assigned to each of the signals. In this case, the values are assigned without the constraint using the shift and & operators: 98 | `out[i] <-- (in >> i) & 1;` 99 | 100 | Afterwards, the constraints need to be defined. In this case, there is a big constraint of the form: 101 | 102 | ``` 103 | in === out[0]*2**0 + out[1]*2**1 + out[2]*2**2 + ... + out[n-1]*2**(n-1) 104 | ``` 105 | 106 | We do this by using a variable `lc1` and adding each signal multiplied by its coefficient. 107 | This variable does not hold a value at compilation time, but it holds a linear combination and it is used in the last constraint: 108 | 109 | ``` 110 | lc1 === in; 111 | ``` 112 | 113 | The last step is to force each output to be binary. This is done by adding the following constraint to each output: 114 | 115 | ``` 116 | out[i] * (out[i] -1 ) === 0; 117 | ``` 118 | 119 | ### A binary adder 120 | 121 | Let's now create a 32bits adder. 122 | 123 | This operation could be done directly by adding a simple constraint `out === in1 + in2`, 124 | but doing this the operation would not be module `2**32` but `r`, where `r`is the range of the elliptic curve. In the case of the zCash current implementation of zkSNARKs this number is typically some prime close to 2**253. 125 | 126 | So, the strategy we will follow will be to first convert a number to binary, then do the addition using the binary representation like in regular electronic circuits, and finally change it back to a number. 127 | 128 | To do this, we create 3 files: `bitify.circom`, `binsum.circom` and `sum_test.circom`. 129 | 130 | bitify.circom: 131 | ``` 132 | template Num2Bits(n) { 133 | signal input in; 134 | signal output out[n]; 135 | var lc1=0; 136 | 137 | for (var i = 0; i> i) & 1; 139 | out[i] * (out[i] -1 ) === 0; 140 | lc1 += out[i] * 2**i; 141 | } 142 | 143 | lc1 === in; 144 | 145 | } 146 | 147 | template Bits2Num(n) { 148 | signal input in[n]; 149 | signal output out; 150 | var lc1=0; 151 | 152 | for (var i = 0; i out; 157 | } 158 | 159 | ``` 160 | 161 | binsum.circom 162 | ``` 163 | /* 164 | 165 | Binary sum 166 | ========== 167 | 168 | This component creates a binary sum componet of ops operands and n bits each operand. 169 | 170 | e is number of carries and it depends on the number of operands in the input. 171 | 172 | Main Constraint: 173 | in[0][0] * 2^0 + in[0][1] * 2^1 + ..... + in[0][n-1] * 2^(n-1) + 174 | + in[1][0] * 2^0 + in[1][1] * 2^1 + ..... + in[1][n-1] * 2^(n-1) + 175 | + .. 176 | + in[ops-1][0] * 2^0 + in[ops-1][1] * 2^1 + ..... + in[ops-1][n-1] * 2^(n-1) + 177 | === 178 | out[0] * 2^0 + out[1] * 2^1 + + out[n+e-1] *2(n+e-1) 179 | 180 | To waranty binary outputs: 181 | 182 | out[0] * (out[0] - 1) === 0 183 | out[1] * (out[0] - 1) === 0 184 | . 185 | . 186 | . 187 | out[n+e-1] * (out[n+e-1] - 1) === 0 188 | 189 | */ 190 | 191 | 192 | /* This function calculates the number of extra bits in the output to do the full sum. */ 193 | 194 | function nbits(a) { 195 | var n = 1; 196 | var r = 0; 197 | while (n-1> k) & 1; 224 | 225 | // Ensure out is binary 226 | out[k] * (out[k] - 1) === 0; 227 | 228 | lout += out[k] * 2**k; 229 | } 230 | 231 | // Ensure the sum 232 | 233 | lin === lout; 234 | } 235 | ``` 236 | 237 | sumtest.circom: 238 | ``` 239 | include "bitify.circom" 240 | include "binsum.circom" 241 | 242 | template Adder() { 243 | signal private input a; 244 | signal input b; 245 | signal output out; 246 | 247 | component n2ba = Num2Bits(32); 248 | component n2bb = Num2Bits(32); 249 | component sum = BinSum(32,2); 250 | component b2n = Bits2Num(32); 251 | 252 | n2ba.in <== a; 253 | n2bb.in <== b; 254 | 255 | for (var i=0; i<32; i++) { 256 | sum.in[0][i] <== n2ba.out[i]; 257 | sum.in[1][i] <== n2bb.out[i]; 258 | b2n.in[i] <== sum.out[i]; 259 | } 260 | 261 | out <== b2n.out; 262 | } 263 | 264 | component main = Adder(); 265 | ``` 266 | 267 | In this example we have shown how to design a top-down circuit with many subcircuits and how to connect them together. One can also see that auxiliary functions to do specific computations can be created. 268 | 269 | 270 | ### More examples. 271 | 272 | You can find more examples in this library of basic circits [circomlib](https://github.com/iden3/circomlib) 273 | 274 | 275 | ## License 276 | 277 | Circom is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details. 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /TUTORIAL.md: -------------------------------------------------------------------------------- 1 | # circom and snarkjs tutorial 2 | 3 | This tutorial will guide you in creating your first zero-knowledge SNARK circuit. It will take you through the various techniques to write circuits and show you how to create and verify proofs off-chain and on-chain on Ethereum. 4 | 5 | ## 1. Installing the tools 6 | 7 | ### 1.1 Pre-requisites 8 | 9 | If you don't have it installed yet, you need to install `Node.js`. 10 | 11 | You should install at least version 10 of node. It's important to note here that the latests versions of javascript, includes big integer support and web assembly compilers that make the code run fast. 12 | 13 | ### 1.2 Install **circom** and **snarkjs** 14 | 15 | Run: 16 | 17 | ```sh 18 | npm install -g circom 19 | npm install -g snarkjs 20 | ``` 21 | 22 | 23 | ## 2. Working with a circuit 24 | 25 | Let's create a circuit that will allow you to prove that you are able to factor a number! 26 | 27 | ### 2.1 Create a circuit in a new directory 28 | 29 | 1. Create an empty directory called `factor` where you will put all the files that you will use in this tutorial. 30 | ``` 31 | mkdir factor 32 | cd factor 33 | ``` 34 | 35 | 36 | > In a real circuit, you will probably want to create a `git` repository with a `circuits` directory and a `test` directory with all your tests, and the needed scripts to build all the circuits. 37 | 38 | 2. Create a new file named `circuit.circom` with the following content: 39 | 40 | ``` 41 | template Multiplier() { 42 | signal private input a; 43 | signal private input b; 44 | signal output c; 45 | 46 | c <== a*b; 47 | } 48 | 49 | component main = Multiplier(); 50 | ``` 51 | 52 | This circuit has 2 private input signals named `a` and `b` and one output named `c`. 53 | 54 | The only thing that the circuit does is forcing the signal `c` to be the value of `a*b` 55 | 56 | After declaring the `Multiplier` template, we instantiate it with a component named`main`. 57 | 58 | Note: When compiling a circuit, a component named `main` must always exist. 59 | 60 | ### 2.2 Compile the circuit 61 | 62 | We are now ready to compile the circuit. Run the following command: 63 | 64 | ```sh 65 | circom circuit.circom --r1cs --wasm --sym 66 | ``` 67 | 68 | The `--r1cs` option will generate `circuit.r1cs` (the r1cs constraint system of the circuit in binary format). 69 | 70 | The `--wasm` option will generate `circuit.wasm` (the wasm code to generate the witness). 71 | 72 | The `--sym` option will generate `circuit.sym` (a symbols file required for debugging or if you want to print the constraint system in an annotated mode). 73 | 74 | 75 | ## 3. Taking the compiled circuit to *snarkjs* 76 | 77 | Now that the circuit is compiled, we will continue with `snarkjs`. 78 | Please note that you can always access the help of `snarkjs` by typing: 79 | 80 | ```sh 81 | snarkjs --help 82 | ``` 83 | 84 | ### 3.1 View information and stats regarding a circuit 85 | 86 | To show general statistics of this circuit, you can run: 87 | 88 | ```sh 89 | snarkjs r1cs info circuit.r1cs 90 | ``` 91 | 92 | You can also print the constraints of the circuit by running: 93 | 94 | ```sh 95 | snarkjs r1cs print circuit.r1cs circuit.sym 96 | ``` 97 | 98 | 99 | ### 3.2 Setting up using *snarkjs* 100 | 101 | 102 | Setup must be a trusted setup ceremony in snarks. 103 | 104 | Please visit [https://github.com/iden3/snarkjs](https://github.com/iden3/snarkjs) to see how to run a ceremony for a real circuit. 105 | 106 | To simplify it, we will run the ceremony ourself. 107 | 108 | 109 | First we download a power of tau ceremony file: 110 | ```sh 111 | wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau 112 | ``` 113 | 114 | Then we create the zkey file withou any contribution 115 | 116 | ```sh 117 | snarkjs zkey new circuit.r1cs powersOfTau28_hez_final_10.ptau circuit_0000.zkey 118 | ``` 119 | 120 | We now add out contribution 121 | 122 | ``` 123 | snarkjs zkey contribute circuit_0000.zkey circuit_final.zkey 124 | ``` 125 | 126 | Finally, we can export the verification key from the zkey file 127 | 128 | ``` 129 | snarkjs zkey export verificationkey circuit_final.zkey verification_key.json 130 | ``` 131 | 132 | ### 3.3. Calculating a witness 133 | 134 | Before creating any proof, we need to calculate all the signals of the circuit that match (all) the constraints of the circuit. 135 | 136 | `circom` generates a wasm module that calculates those for you. You need to provide a file with the inputs and it will execute the circuit and calculate all the intermediate signals and the output. This set of signals is the *witness*. 137 | 138 | The zero-knowledge proofs prove that you know a set of signals (witness) that match all the constraints without revealing any of the signals except the public inputs and the outputs. 139 | 140 | For example, imagine you want to prove you are able to factor the number 33. It means that you know two numbers `a` and `b` that when you multiply them, it results in 33. 141 | 142 | > Of course you can always use the number one and the same number as `a` or `b`. We will deal with this problem later. 143 | 144 | So you want to prove that you know 3 and 11. 145 | 146 | Let's create a file named `input.json` 147 | 148 | ```json 149 | {"a": 3, "b": 11} 150 | ``` 151 | 152 | Now let's calculate the witness: 153 | 154 | ```sh 155 | snarkjs wtns calculate circuit.wasm input.json witness.wtns 156 | ``` 157 | 158 | To see the witness.wtns file, you can export it to jeson and take a look 159 | 160 | ```sh 161 | snarkjs wtns export json witness.wtns witness.json 162 | cat witness.json 163 | ``` 164 | 165 | If the circuit has any error, you can debug the generation of the witness with 166 | ```sh 167 | snarkjs wtns debug circuit.wasm input.json witness.wtns circuit.sym 168 | ``` 169 | 170 | ### Create the proof 171 | 172 | Now that we have the witness generated, we can create the proof. 173 | 174 | ```sh 175 | snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json 176 | ``` 177 | 178 | This command will use the `circuit_final.zkey` and the `witness.wtns` files by default to generate `proof.json` and `public.json` 179 | 180 | The `proof.json` file will contain the actual proof and the `public.json` file will contain just the values of the public inputs and the outputs. 181 | 182 | 183 | ### Verifying the proof 184 | 185 | To verify the proof run: 186 | 187 | ```sh 188 | snarkjs groth16 verify verification_key.json public.json proof.json 189 | ``` 190 | 191 | This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid. 192 | 193 | Here we are verifying that we know a witness that the public inputs and the outputs matches the ones in the `public.json` file. 194 | 195 | 196 | If the proof is ok, you will see `OK` or `INVALID` if not ok. 197 | 198 | ### Generate the solidity verifier 199 | 200 | ```sh 201 | snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol 202 | ``` 203 | 204 | This command will take the `circuit_final.zkey` and generate solidity code in `verifier.sol` file. 205 | 206 | You can take the code in `verifier.sol` and cut and paste it in remix. 207 | 208 | This code contains two contracts: Pairings and Verifier. You only need to deploy the `Verifier` contract. 209 | 210 | > You may want to use a test net like Rinkeby, Kovan or Ropsten. You can also use the Javascript VM, but in some browsers the verification takes long and it may hang the page. 211 | 212 | 213 | ### Verifying the proof on-chain 214 | 215 | The verifier contract deployed in the last step has a `view` function called `verifyProof`. 216 | 217 | This function will return true if the proof and the inputs are valid. 218 | 219 | To facilitate the call, you can use `snarkjs` to generate the parameters of the call by typing: 220 | 221 | ```sh 222 | snarkjs zkey export soliditycalldata public.json proof.json 223 | ``` 224 | 225 | Just cut and paste the output to the parameters field of the `verifyProof` method in Remix. 226 | 227 | If every thing works ok, this method should return true. 228 | 229 | If you change any bit in the parameters, the result will be verifiably false. 230 | 231 | 232 | ## Bonus track 233 | 234 | We can fix the circuit to not accept the number 1 as any of the input values by adding some extra constraints. 235 | 236 | Here, the trick is that we use the property that 0 has no inverse. So `(a-1)` should not have an inverse. 237 | 238 | That means that `(a-1)*inv = 1` will be inpossible to match if `a` is 1. 239 | 240 | We just calculate inv by `1/(a-1)`. 241 | 242 | So, let's modify the circuit: 243 | 244 | ``` 245 | template Multiplier() { 246 | signal private input a; 247 | signal private input b; 248 | signal output c; 249 | signal inva; 250 | signal invb; 251 | 252 | inva <-- 1/(a-1); 253 | (a-1)*inva === 1; 254 | 255 | invb <-- 1/(b-1); 256 | (b-1)*invb === 1; 257 | 258 | c <== a*b; 259 | } 260 | 261 | component main = Multiplier(); 262 | ``` 263 | 264 | A nice thing of the circom language is that you can split a `<==` into two independent actions: `<--` and `===`. 265 | 266 | The `<--` and `-->` operators assign a value to a signal without creating any constraints. 267 | 268 | The `===` operator adds a constraint without assigning any value to a signal. 269 | 270 | The circuit also has another problem: the operation works in `Z_r`, so we need to guarantee the multiplication does not overflow. This can be done by converting the inputs to binary and checking the ranges, but we will reserve it for future tutorials. 271 | 272 | Another problem of the circuit is that circom works with a field of a prime that in general is arround the 255bits. That means that it's very easy to factor in that field. 273 | 274 | One possible solution to this, is to limit the inputs to 64 bits. that means that this way it will not be possible to have overflow. 275 | 276 | The final circuit would look like: 277 | 278 | ``` 279 | template CheckBits(n) { 280 | signal input in; 281 | signal bits[n]; 282 | var lc1=0; 283 | 284 | var e2=1; 285 | for (var i = 0; i> i) & 1; 287 | bits[i] * (bits[i] -1 ) === 0; 288 | lc1 += bits[i] * e2; 289 | e2 = e2+e2; 290 | } 291 | 292 | lc1 === in; 293 | } 294 | 295 | template Multiplier(n) { 296 | signal private input a; 297 | signal private input b; 298 | signal output c; 299 | signal inva; 300 | signal invb; 301 | 302 | component chackA = CheckBits(n); 303 | component chackB = CheckBits(n); 304 | 305 | chackA.in <== a; 306 | chackB.in <== b; 307 | 308 | inva <-- 1/(a-1); 309 | (a-1)*inva === 1; 310 | 311 | invb <-- 1/(b-1); 312 | (b-1)*invb === 1; 313 | 314 | c <== a*b; 315 | } 316 | 317 | component main = Multiplier(64); 318 | ``` 319 | 320 | ## Where to go from here 321 | 322 | You may want to read the [README](https://github.com/iden3/circom) to learn more features about `circom`. 323 | 324 | You can also check a library with many basic circuits lib binarizations, comparators, eddsa, hashes, merkle trees etc [here](https://github.com/iden3/circomlib) (Work in progress). 325 | 326 | 327 | Or a exponentiation in the Baby Jubjub curve [here](https://github.com/iden3/circomlib) (Work in progress). 328 | 329 | 330 | # Final note 331 | 332 | There is nothing worse for a dev than working with a buggy compiler. This is a very early stage of the compiler, so there are many bugs and lots of work needs to be done. Please have it present if you are doing anything serious with it. 333 | 334 | And please contact us for any isue you have. In general, a github issue with a small piece of code with the bug is very useful to us. 335 | 336 | Enjoy zero-knowledge proving! 337 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | Copyright 2018 0KIMS association. 5 | 6 | This file is part of circom (Zero Knowledge Circuit Compiler). 7 | 8 | circom is a free software: you can redistribute it and/or modify it 9 | under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | circom is distributed in the hope that it will be useful, but WITHOUT 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 | License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with circom. If not, see . 20 | */ 21 | 22 | /* eslint-disable no-console */ 23 | 24 | const fs = require("fs"); 25 | const path = require("path"); 26 | const Scalar = require("ffjavascript").Scalar; 27 | const stringifyBigInts = require("ffjavascript").utils.stringifyBigInts; 28 | 29 | const compiler = require("./src/compiler"); 30 | 31 | const version = require("./package").version; 32 | 33 | const argv = require("yargs") 34 | .version(version) 35 | .usage("circom [input source circuit file] -r [output r1cs file] -c [output c file] -w [output wasm file] -t [output wat file] -s [output sym file]") 36 | .alias("o", "output") 37 | .alias("c", "csource") 38 | .alias("w", "wasm") 39 | .alias("t", "wat") 40 | .alias("s", "sym") 41 | .alias("r", "r1cs") 42 | .alias("p", "prime") 43 | .alias("n", "newThreadTemplates") 44 | .help("h") 45 | .alias("h", "help") 46 | .option("verbose", { 47 | alias: "v", 48 | type: "boolean", 49 | description: "Run with verbose logging" 50 | }) 51 | .option("fast", { 52 | alias: "f", 53 | type: "boolean", 54 | description: "Do not optimize constraints" 55 | }) 56 | .epilogue(`Copyright (C) 2018 0kims association 57 | This program comes with ABSOLUTELY NO WARRANTY; 58 | This is free software, and you are welcome to redistribute it 59 | under certain conditions; see the COPYING file in the official 60 | repo directory at https://github.com/iden3/circom `) 61 | .argv; 62 | 63 | 64 | async function run() { 65 | let inputFile; 66 | if (argv._.length == 0) { 67 | inputFile = "circuit.circom"; 68 | } else if (argv._.length == 1) { 69 | inputFile = argv._[0]; 70 | } else { 71 | console.log("Only one circuit at a time is permited"); 72 | process.exit(1); 73 | } 74 | 75 | const fullFileName = path.resolve(process.cwd(), inputFile); 76 | const fileName = path.basename(fullFileName, ".circom"); 77 | const cSourceName = typeof(argv.csource) === "string" ? argv.csource : fileName + ".cpp"; 78 | const wasmName = typeof(argv.wasm) === "string" ? argv.wasm : fileName + ".wasm"; 79 | const watName = typeof(argv.wat) === "string" ? argv.wat : fileName + ".wat"; 80 | const r1csName = typeof(argv.r1cs) === "string" ? argv.r1cs : fileName + ".r1cs"; 81 | const symName = typeof(argv.sym) === "string" ? argv.sym : fileName + ".sym"; 82 | 83 | const options = {}; 84 | options.reduceConstraints = !argv.fast; 85 | options.verbose = argv.verbose || false; 86 | options.sanityCheck = argv.sanitycheck; 87 | 88 | if (argv.csource) { 89 | options.cSourceFileName = cSourceName; 90 | const noExt = cSourceName.substr(0, cSourceName.lastIndexOf(".")) || cSourceName; 91 | options.dataFileName = noExt+".dat"; 92 | } 93 | if (argv.wasm) { 94 | options.wasmFileName = wasmName; 95 | } 96 | if (argv.wat) { 97 | options.watFileName = watName; 98 | } 99 | if (argv.r1cs) { 100 | options.r1csFileName = r1csName; 101 | } 102 | if (argv.sym) { 103 | options.symWriteStream = fs.createWriteStream(symName); 104 | } 105 | if (argv.newThreadTemplates) { 106 | options.newThreadTemplates = new RegExp(argv.newThreadTemplates); 107 | } 108 | if (!argv.prime) { 109 | options.prime = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 110 | } else if (["BLS12-381", "BLS12381"]. indexOf(argv.prime.toUpperCase()) >=0) { 111 | options.prime = Scalar.fromString("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001",16); 112 | } else if (["BN-128", "BN128", "BN254", "BN-254"]. indexOf(argv.prime.toUpperCase()) >=0) { 113 | options.prime = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 114 | } else { 115 | options.prime = Scalar.fromString(argv.prime); 116 | } 117 | 118 | 119 | await compiler(fullFileName, options); 120 | 121 | let symDone = false; 122 | if (options.symWriteStream) { 123 | options.symWriteStream.on("finish", () => { 124 | symDone = true; 125 | finishIfDone(); 126 | }); 127 | } else { 128 | symDone = true; 129 | } 130 | function finishIfDone() { 131 | if (symDone) { 132 | setTimeout(() => { 133 | process.exit(0); 134 | }, 300); 135 | } 136 | } 137 | 138 | } 139 | 140 | run().then(()=> { 141 | process.exit(0); 142 | }, (err) => { 143 | // console.log(err); 144 | console.log(err.stack); 145 | if (err.pos) { 146 | console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`); 147 | } else { 148 | console.log(err.message); 149 | if (argv.verbose) console.log(err.stack); 150 | } 151 | if (err.ast) { 152 | console.error(JSON.stringify(stringifyBigInts(err.ast), null, 1)); 153 | } 154 | process.exit(1); 155 | }); 156 | 157 | 158 | -------------------------------------------------------------------------------- /doc/lc_example.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/circom_old/6742455b5ec8d93de31db78b8d82347c1b6a75ef/doc/lc_example.monopic -------------------------------------------------------------------------------- /doc/wasmmodule.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | (module 4 | (export "get_signal_ptr" (func $get_signal_ptr)) 5 | (export "solve" (func $solve)) 6 | (func $get_signal_ptr (result i32) 7 | (i32.const 22) 8 | ) 9 | (func $solve (result i32) 10 | (i32.const 22) 11 | ) 12 | ) 13 | 14 | */ 15 | 16 | 17 | typedef int u32; 18 | typedef u32 u256[8]; 19 | 20 | struct Signals { 21 | u256 one; 22 | u256 output1; 23 | u256 output2; 24 | u256 pubInput1; 25 | u256 pubInput2; 26 | u256 prvInput1; 27 | u256 prvInput2; 28 | u256 internal1; 29 | u256 internal2; 30 | }; 31 | 32 | static Signals signals; 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | u256 *get_signal_ptr() { 39 | return (u256 *)&signals; 40 | } 41 | 42 | int solve() { 43 | for (int i=0; i<8; i++) { 44 | signals.internal1[i] = signals.pubInput1[i]; 45 | signals.internal2[i] = signals.pubInput2[i]; 46 | signals.output1[i] = signals.prvInput1[i]; 47 | signals.output2[i] = signals.prvInput2[i]; 48 | } 49 | } 50 | 51 | #ifdef __cplusplus 52 | } // extern "C" 53 | #endif 54 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports.compiler = require("./src/compiler.js"); 2 | module.exports.c_tester = require("./ports/c/tester.js"); 3 | module.exports.wasm_tester = require("./ports/wasm/tester.js"); 4 | module.exports.tester = module.exports.wasm_tester; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "circom", 3 | "version": "0.5.46", 4 | "description": "Language to generate logic circuits", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha --max-old-space-size=4000", 11 | "buildParser": "jison parser/jaz.jison -o parser/jaz.js" 12 | }, 13 | "bin": { 14 | "circom": "cli.js" 15 | }, 16 | "keywords": [ 17 | "zkSnarks", 18 | "r1cs", 19 | "circuits", 20 | "zero", 21 | "knowlage", 22 | "ethereum", 23 | "zcash" 24 | ], 25 | "author": "0kims", 26 | "license": "GPL-3.0", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/iden3/circom_old.git" 30 | }, 31 | "dependencies": { 32 | "chai": "^4.2.0", 33 | "circom_runtime": "0.1.12", 34 | "fastfile": "0.0.18", 35 | "ffiasm": "0.1.1", 36 | "ffjavascript": "0.2.22", 37 | "ffwasm": "0.0.7", 38 | "fnv-plus": "^1.3.1", 39 | "r1csfile": "0.0.16", 40 | "tmp-promise": "^2.0.2", 41 | "wasmbuilder": "0.0.10" 42 | }, 43 | "devDependencies": { 44 | "eslint": "^6.8.0", 45 | "jison": "^0.4.18", 46 | "yargs": "^15.3.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /parser/jaz.jison: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | 20 | /* description: Construct AST for jaz language. */ 21 | 22 | /* lexical grammar */ 23 | %lex 24 | 25 | %% 26 | 27 | \s+ { /* skip whitespace */ } 28 | \/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/ { /* console.log("MULTILINE COMMENT: "+yytext); */ } 29 | \/\/.* { /* console.log("SINGLE LINE COMMENT: "+yytext); */ } 30 | var { return 'var'; } 31 | signal { return 'signal'; } 32 | private { return 'private'; } 33 | input { return 'input'; } 34 | output { return 'output'; } 35 | linearCombination { return 'linearCombination'; } 36 | component { return 'component'; } 37 | template { return 'template'; } 38 | function { return 'function'; } 39 | if { return 'if'; } 40 | else { return 'else'; } 41 | for { return 'for'; } 42 | while { return 'while'; } 43 | compute { return 'compute'; } 44 | do { return 'do'; } 45 | return { return 'return'; } 46 | include { return 'include'; } 47 | 0x[0-9A-Fa-f]* { return 'HEXNUMBER'; } 48 | [0-9]+ { return 'DECNUMBER'; } 49 | [a-zA-Z][a-zA-Z$_0-9]* { return 'IDENTIFIER'; } 50 | \"[^"]+\" { yytext = yytext.slice(1,-1); return 'STRING'; } 51 | \=\=\> { return '==>'; } 52 | \<\=\= { return '<=='; } 53 | \-\-\> { return '-->'; } 54 | \<\-\- { return '<--'; } 55 | \=\=\= { return '==='; } 56 | \>\>\= { return '>>='; } 57 | \<\<\= { return '<<='; } 58 | \&\& { return '&&'; } 59 | \|\| { return '||'; } 60 | \=\= { return '=='; } 61 | \<\= { return '<='; } 62 | \>\= { return '>='; } 63 | \!\= { return '!='; } 64 | \>\> { return '>>'; } 65 | \<\< { return '<<'; } 66 | \*\* { return '**'; } 67 | \+\+ { return '++'; } 68 | \-\- { return '--'; } 69 | \+\= { return '+='; } 70 | \-\= { return '-='; } 71 | \*\= { return '*='; } 72 | \/\= { return '/='; } 73 | \%\= { return '%='; } 74 | \|\= { return '|='; } 75 | \&\= { return '&='; } 76 | \^\= { return '^='; } 77 | \= { return '='; } 78 | \+ { return '+'; } 79 | \- { return '-'; } 80 | \* { return '*'; } 81 | \/ { return '/'; } 82 | \\ { return '\\'; } 83 | \% { return '%'; } 84 | \^ { return '^'; } 85 | \& { return '&'; } 86 | \| { return '|'; } 87 | \! { return '!'; } 88 | \~ { return '~'; } 89 | \< { return '<'; } 90 | \> { return '>'; } 91 | \! { return '!'; } 92 | \? { return '?'; } 93 | \: { return ':'; } 94 | \( { return '('; } 95 | \) { return ')'; } 96 | \[ { return '['; } 97 | \] { return ']'; } 98 | \{ { return '{'; } 99 | \} { return '}'; } 100 | \; { return ';'; } 101 | \, { return ','; } 102 | \. { return '.'; } 103 | <> { return 'EOF'; } 104 | . { console.log("INVALID: " + yytext); return 'INVALID'} 105 | 106 | /lex 107 | 108 | %left ';' 109 | %right 'if' 'else' 110 | %left EMPTY 111 | %left IDLIST 112 | %left ',' 113 | %right '?' ':' TERCOND '=' '+=' '-=' '*=' '/=' '%=' '>>=' '<<=' '&=' '|=' '^=' '<==' '==>' '===' '<--' '-->' 114 | %left '||' 115 | %left '&&' 116 | %left '|' 117 | %left '^' 118 | %left '&' 119 | %left '==' '!=' 120 | %left '<=' '>=' '<' '>' 121 | %left '<<' '>>' 122 | 123 | %left '+' '-' 124 | %left '*' '/' '\\' '%' 125 | %left '**' 126 | %right '++' '--' UMINUS UPLUS '!' '~' 127 | %left '.' 128 | %left DECL 129 | %left PLUSPLUSRIGHT MINUSMINUSRIGHT '[' ']' '(' ')' 130 | %left HIGH 131 | 132 | 133 | 134 | %{ 135 | const Scalar = require('ffjavascript').Scalar; 136 | const util = require('util'); 137 | 138 | function setLines(dst, first, last) { 139 | last = last || first; 140 | dst.first_line = first.first_line; 141 | dst.first_column = first.first_column; 142 | dst.last_line = last.last_line; 143 | dst.last_column = last.last_column; 144 | } 145 | 146 | %} 147 | 148 | %start allStatments 149 | 150 | %% /* language grammar */ 151 | 152 | 153 | allStatments 154 | : statmentList EOF 155 | { 156 | // console.log(JSON.stringify($1, null, 1)); 157 | $$ = { type: "BLOCK", statements: $1.statments }; 158 | setLines($$, @1); 159 | return $$ 160 | } 161 | ; 162 | 163 | statmentList 164 | : statmentList statment 165 | { 166 | $1.statments.push($2); 167 | setLines($1, @1, @2); 168 | } 169 | | statment 170 | { 171 | $$ = { type: "STATMENTLIST", statments: [$1] }; 172 | setLines($$, @1); 173 | } 174 | ; 175 | 176 | statment 177 | : functionDefinitionStatment 178 | { 179 | $$ = $1; 180 | } 181 | | templateDefinitionStatment 182 | { 183 | $$ = $1; 184 | } 185 | | ifStatment 186 | { 187 | $$ = $1; 188 | } 189 | | forStatment 190 | { 191 | $$ = $1; 192 | } 193 | | whileStatment 194 | { 195 | $$ = $1; 196 | } 197 | | doWhileStatment 198 | { 199 | $$ = $1; 200 | } 201 | | computeStatment 202 | { 203 | $$ = $1; 204 | } 205 | | returnStatment 206 | { 207 | $$ = $1; 208 | } 209 | | block 210 | { 211 | $$ = $1; 212 | } 213 | | expressionStatment 214 | { 215 | $$ = $1; 216 | } 217 | | includeStatment 218 | { 219 | $$ = $1; 220 | } 221 | ; 222 | 223 | 224 | functionDefinitionStatment 225 | : 'function' IDENTIFIER '(' identifierList ')' block 226 | { 227 | $$ = { type: "FUNCTIONDEF", name: $2, params: $4.identifiers, block: $6}; 228 | setLines($$, @1, @6); 229 | } 230 | | 'function' IDENTIFIER '(' ')' block 231 | { 232 | $$ = { type: "FUNCTIONDEF", name: $2, params: [], block: $5 }; 233 | setLines($$, @1, @5); 234 | } 235 | ; 236 | 237 | templateDefinitionStatment 238 | : 'template' IDENTIFIER '(' identifierList ')' block 239 | { 240 | $$ = { type: "TEMPLATEDEF", name: $2, params: $4.identifiers, block: $6 }; 241 | setLines($$, @1, @6); 242 | } 243 | | 'template' IDENTIFIER '(' ')' block 244 | { 245 | $$ = { type: "TEMPLATEDEF", name: $2, params: [], block: $5 }; 246 | setLines($$, @1, @5); 247 | } 248 | ; 249 | 250 | 251 | identifierList 252 | : identifierList ',' IDENTIFIER 253 | { 254 | $1.identifiers.push($3); 255 | setLines($1, @1, @3); 256 | } 257 | | IDENTIFIER %prec EMPTY 258 | { 259 | $$ = { type: "IDENTIFIERLIST", identifiers: [$1] }; 260 | setLines($$, @1); 261 | } 262 | ; 263 | 264 | ifStatment 265 | : 'if' '(' expression ')' statment 'else' statment 266 | { 267 | $$ = { type: "IF", condition: $3, then: $5, else: $7 }; 268 | setLines($$, @1, @7); 269 | } 270 | | 'if' '(' expression ')' statment 271 | { 272 | $$ = { type: "IF", condition: $3, then: $5 }; 273 | setLines($$, @1, @5); 274 | } 275 | ; 276 | 277 | forStatment 278 | : 'for' '(' expression ';' expression ';' expression ')' statment 279 | { 280 | $$ = { type: "FOR", init: $3, condition: $5, step: $7, body: $9 }; 281 | setLines($$, @1, @9); 282 | } 283 | ; 284 | 285 | whileStatment 286 | : 'while' '(' expression ')' statment 287 | { 288 | $$ = { type: "WHILE", condition: $3, body: $5 }; 289 | setLines($$, @1, @5); 290 | } 291 | ; 292 | 293 | doWhileStatment 294 | : 'do' statment 'while' '(' expression ')' 295 | { 296 | $$ = { type: "DOWHILE", condition: $5, body: $2 }; 297 | setLines($$, @1, @6); 298 | } 299 | ; 300 | 301 | computeStatment 302 | : 'compute' statment 303 | { 304 | $$ = { type: "COMPUTE", body: $2 }; 305 | setLines($$, @1, @2); 306 | } 307 | ; 308 | 309 | returnStatment 310 | : 'return' expression ';' 311 | { 312 | $$ = { type: "RETURN", value: $2 }; 313 | setLines($$, @1, @3); 314 | } 315 | | 'return' expression %prec ';' 316 | { 317 | $$ = { type: "RETURN", value: $2 } 318 | setLines($$, @1, @2); 319 | } 320 | ; 321 | 322 | includeStatment 323 | : 'include' STRING ';' 324 | { 325 | $$ = { type: "INCLUDE", file: $2 }; 326 | setLines($$, @1, @3); 327 | } 328 | | 'include' STRING %prec ';' 329 | { 330 | $$ = { type: "INCLUDE", file: $2 } 331 | setLines($$, @1, @2); 332 | } 333 | ; 334 | 335 | block 336 | : '{' statmentList '}' 337 | { 338 | $$ = { type: "BLOCK", statements: $2.statments }; 339 | setLines($$, @1, @3); 340 | } 341 | ; 342 | 343 | expressionStatment 344 | : expression ';' %prec ';' 345 | { 346 | $$ = $1; 347 | } 348 | | expression %prec ';' 349 | { 350 | $$ = $1; 351 | } 352 | ; 353 | 354 | expression 355 | : e17 %prec EMPTY 356 | { 357 | $$ = $1; 358 | } 359 | ; 360 | 361 | e17 362 | : leftHandExpression '=' e17 363 | { 364 | $$ = { type: "OP", op: "=", values: [$1, $3] }; 365 | setLines($$, @1, @3); 366 | } 367 | | leftHandExpression '+=' e17 368 | { 369 | $$ = { type: "OP", op: "+=", values: [$1, $3] }; 370 | setLines($$, @1, @3); 371 | } 372 | | leftHandExpression '-=' e17 373 | { 374 | $$ = { type: "OP", op: "-=", values: [$1, $3] }; 375 | setLines($$, @1, @3); 376 | } 377 | | leftHandExpression '*=' e17 378 | { 379 | $$ = { type: "OP", op: "*=", values: [$1, $3] }; 380 | setLines($$, @1, @3); 381 | } 382 | | leftHandExpression '/=' e17 383 | { 384 | $$ = { type: "OP", op: "/=", values: [$1, $3] }; 385 | setLines($$, @1, @3); 386 | } 387 | | leftHandExpression '%=' e17 388 | { 389 | $$ = { type: "OP", op: "%=", values: [$1, $3] }; 390 | setLines($$, @1, @3); 391 | } 392 | | leftHandExpression '<<=' e17 393 | { 394 | $$ = { type: "OP", op: "<<=", values: [$1, $3] }; 395 | setLines($$, @1, @3); 396 | } 397 | | leftHandExpression '>>=' e17 398 | { 399 | $$ = { type: "OP", op: ">>=", values: [$1, $3] }; 400 | setLines($$, @1, @3); 401 | } 402 | | leftHandExpression '&=' e17 403 | { 404 | $$ = { type: "OP", op: "&=", values: [$1, $3] }; 405 | setLines($$, @1, @3); 406 | } 407 | | leftHandExpression '|=' e17 408 | { 409 | $$ = { type: "OP", op: "|=", values: [$1, $3] }; 410 | setLines($$, @1, @3); 411 | } 412 | | leftHandExpression '^=' e17 413 | { 414 | $$ = { type: "OP", op: "^=", values: [$1, $3] }; 415 | setLines($$, @1, @3); 416 | } 417 | | leftHandExpression '<==' e17 418 | { 419 | $$ = { type: "OP", op: "<==", values: [$1, $3] }; 420 | setLines($$, @1, @3); 421 | } 422 | | e17 '==>' leftHandExpression 423 | { 424 | $$ = { type: "OP", op: "<==", values: [$3, $1] }; 425 | setLines($$, @1, @3); 426 | } 427 | | leftHandExpression '<--' e17 428 | { 429 | $$ = { type: "OP", op: "<--", values: [$1, $3] }; 430 | setLines($$, @1, @3); 431 | } 432 | | e17 '-->' leftHandExpression 433 | { 434 | $$ = { type: "OP", op: "<--", values: [$3, $1] }; 435 | setLines($$, @1, @3); 436 | } 437 | | e16 '===' e17 438 | { 439 | $$ = { type: "OP", op: "===", values: [$1, $3] }; 440 | setLines($$, @1, @3); 441 | } 442 | | e17 '?' e17 ':' e17 %prec TERCOND 443 | { 444 | $$ = { type: "OP", op: "?", values: [$1, $3, $5] }; 445 | setLines($$, @1, @5); 446 | } 447 | | e16 %prec EMPTY 448 | { 449 | $$ = $1; 450 | } 451 | ; 452 | 453 | e16 454 | : rightArray 455 | { 456 | $$ = $1; 457 | } 458 | | e15 %prec EMPTY 459 | { 460 | $$ = $1; 461 | } 462 | ; 463 | 464 | e15 465 | : e15 '||' e14 466 | { 467 | $$ = { type: "OP", op: "||", values: [$1, $3] }; 468 | setLines($$, @1, @3); 469 | } 470 | | e14 %prec EMPTY 471 | { 472 | $$ = $1; 473 | } 474 | ; 475 | 476 | e14 477 | : e14 '&&' e13 478 | { 479 | $$ = { type: "OP", op: "&&", values: [$1, $3] }; 480 | setLines($$, @1, @3); 481 | } 482 | | e13 %prec EMPTY 483 | { 484 | $$ = $1; 485 | } 486 | ; 487 | 488 | e13 489 | : e13 '|' e12 490 | { 491 | $$ = { type: "OP", op: "|", values: [$1, $3] }; 492 | setLines($$, @1, @3); 493 | } 494 | | e12 %prec EMPTY 495 | { 496 | $$ = $1; 497 | } 498 | ; 499 | 500 | 501 | e12 502 | : e12 '^' e11 503 | { 504 | $$ = { type: "OP", op: "^", values: [$1, $3] }; 505 | setLines($$, @1, @3); 506 | } 507 | | e11 %prec EMPTY 508 | { 509 | $$ = $1; 510 | } 511 | ; 512 | 513 | e11 514 | : e11 '&' e10 515 | { 516 | $$ = { type: "OP", op: "&", values: [$1, $3] }; 517 | setLines($$, @1, @3); 518 | } 519 | | e10 %prec EMPTY 520 | { 521 | $$ = $1; 522 | } 523 | ; 524 | 525 | 526 | 527 | 528 | e10 529 | : e10 '==' e9 530 | { 531 | $$ = { type: "OP", op: "==", values: [$1, $3] }; 532 | setLines($$, @1, @3); 533 | } 534 | | e10 '!=' e9 535 | { 536 | $$ = { type: "OP", op: "!=", values: [$1, $3] }; 537 | setLines($$, @1, @3); 538 | } 539 | | e9 %prec EMPTY 540 | { 541 | $$ = $1 542 | } 543 | ; 544 | 545 | e9 546 | : e9 '<=' e7 547 | { 548 | $$ = { type: "OP", op: "<=", values: [$1, $3] }; 549 | setLines($$, @1, @3); 550 | } 551 | | e9 '>=' e7 552 | { 553 | $$ = { type: "OP", op: ">=", values: [$1, $3] }; 554 | setLines($$, @1, @3); 555 | } 556 | | e9 '<' e7 557 | { 558 | $$ = { type: "OP", op: "<", values: [$1, $3] }; 559 | setLines($$, @1, @3); 560 | } 561 | | e9 '>' e7 562 | { 563 | $$ = { type: "OP", op: ">", values: [$1, $3] }; 564 | setLines($$, @1, @3); 565 | } 566 | | e7 %prec EMPTY 567 | { 568 | $$ = $1 569 | } 570 | ; 571 | 572 | e7 573 | : e7 '<<' e6 574 | { 575 | $$ = { type: "OP", op: "<<", values: [$1, $3] }; 576 | setLines($$, @1, @3); 577 | } 578 | | e7 '>>' e6 579 | { 580 | $$ = { type: "OP", op: ">>", values: [$1, $3] }; 581 | setLines($$, @1, @3); 582 | } 583 | | e6 %prec EMPTY 584 | { 585 | $$ = $1; 586 | } 587 | ; 588 | 589 | e6 590 | : e6 '+' e5 591 | { 592 | $$ = { type: "OP", op: "+", values: [$1, $3] }; 593 | setLines($$, @1, @3); 594 | } 595 | | e6 '-' e5 596 | { 597 | $$ = { type: "OP", op: "-", values: [$1, $3] }; 598 | setLines($$, @1, @3); 599 | } 600 | | e5 %prec EMPTY 601 | { 602 | $$ = $1; 603 | } 604 | ; 605 | 606 | 607 | e5 608 | : e5 '*' e4 609 | { 610 | $$ = { type: "OP", op: "*", values: [$1, $3] }; 611 | setLines($$, @1, @3); 612 | } 613 | | e5 '/' e4 614 | { 615 | $$ = { type: "OP", op: "/", values: [$1, $3] }; 616 | setLines($$, @1, @3); 617 | } 618 | | e5 '\\' e4 619 | { 620 | $$ = { type: "OP", op: "\\", values: [$1, $3] }; 621 | setLines($$, @1, @3); 622 | } 623 | | e5 '%' e4 624 | { 625 | $$ = { type: "OP", op: "%", values: [$1, $3] }; 626 | setLines($$, @1, @3); 627 | } 628 | | e4 %prec EMPTY 629 | { 630 | $$ = $1; 631 | } 632 | ; 633 | 634 | e4 635 | : e4 '**' e3 636 | { 637 | $$ = { type: "OP", op: "**", values: [$1, $3] }; 638 | setLines($$, @1, @3); 639 | } 640 | | e3 %prec EMPTY 641 | { 642 | $$ = $1; 643 | } 644 | ; 645 | 646 | 647 | e3 648 | : '++' leftHandExpression 649 | { 650 | $$ = { type: "OP", op: "PLUSPLUSLEFT", values: [$2] }; 651 | setLines($$, @1, @2); 652 | } 653 | | '--' leftHandExpression 654 | { 655 | $$ = { type: "OP", op: "MINUSMINUSLEFT", values: [$2] }; 656 | setLines($$, @1, @2); 657 | } 658 | | '+' e3 %prec UPLUS 659 | { 660 | $$ = $2; 661 | setLines($$, @1, @2); 662 | } 663 | | '-' e3 %prec UMINUS 664 | { 665 | $$ = { type: "OP", op: "UMINUS", values: [$2] }; 666 | setLines($$, @1, @2); 667 | } 668 | | '!' e3 669 | { 670 | $$ = { type: "OP", op: "!", values: [$2] }; 671 | setLines($$, @1, @2); 672 | } 673 | | '~' e3 674 | { 675 | $$ = { type: "OP", op: "~", values: [$2] }; 676 | setLines($$, @1, @2); 677 | } 678 | | e2 %prec EMPTY 679 | { 680 | $$ = $1; 681 | } 682 | ; 683 | 684 | e2 685 | : leftHandExpression '++' %prec PLUSPLUSRIGHT 686 | { 687 | $$ = {type: "OP", op: "PLUSPLUSRIGHT", values: [$1] }; 688 | setLines($$, @1, @2); 689 | } 690 | | leftHandExpression '--' %prec MINUSMINUSRIGHT 691 | { 692 | $$ = {type: "OP", op: "MINUSMINUSRIGHT", values: [$1] }; 693 | setLines($$, @1, @2); 694 | } 695 | | functionCall 696 | { 697 | $$ = $1; 698 | } 699 | | e0 %prec EMPTY 700 | { 701 | $$ = $1; 702 | } 703 | ; 704 | 705 | e0 706 | : leftHandExpression %prec EMPTY 707 | { 708 | $$ = $1 709 | } 710 | | DECNUMBER 711 | { 712 | $$ = {type: "NUMBER", value: Scalar.fromString($1) } 713 | setLines($$, @1); 714 | } 715 | | HEXNUMBER 716 | { 717 | $$ = {type: "NUMBER", value: Scalar.fromString($1.substr(2).toUpperCase(), 16) } 718 | setLines($$, @1); 719 | } 720 | | '(' expression ')' %prec EMPTY 721 | { 722 | $$ = $2; 723 | setLines($$, @1, @3); 724 | } 725 | ; 726 | 727 | leftHandExpression 728 | : simpleLeftHandExpression '.' simpleLeftHandExpression %prec EMPTY 729 | { 730 | $$ = {type: "PIN", component: $1, pin: $3 }; 731 | setLines($$, @1, @3); 732 | } 733 | | declaration %prec DECL 734 | { 735 | $$ = $1 736 | } 737 | | simpleLeftHandExpression %prec EMPTY 738 | { 739 | $$ = $1 740 | } 741 | ; 742 | 743 | 744 | 745 | declaration 746 | : 'var' simpleLeftHandExpression %prec DECL 747 | { 748 | $$ = {type: "DECLARE", declareType: "VARIABLE", name: $2} 749 | setLines($$, @1, @2); 750 | } 751 | | 'signal' simpleLeftHandExpression %prec DECL 752 | { 753 | $$ = {type: "DECLARE", declareType: "SIGNAL", name: $2} 754 | setLines($$, @1, @2); 755 | } 756 | | 'signal' 'input' simpleLeftHandExpression %prec DECL 757 | { 758 | $$ = {type: "DECLARE", declareType: "SIGNALIN", name: $3}; 759 | setLines($$, @1, @3); 760 | } 761 | | 'signal' 'private' 'input' simpleLeftHandExpression %prec DECL 762 | { 763 | $$ = {type: "DECLARE", declareType: "SIGNALIN", private: true, name: $4}; 764 | setLines($$, @1, @4); 765 | } 766 | | 'signal' 'output' simpleLeftHandExpression %prec DECL 767 | { 768 | $$ = {type: "DECLARE", declareType: "SIGNALOUT", name: $3}; 769 | setLines($$, @1, @3); 770 | } 771 | | 'component' simpleLeftHandExpression %prec DECL 772 | { 773 | $$ = {type: "DECLARE", declareType: "COMPONENT", name: $2} 774 | setLines($$, @1, @2); 775 | } 776 | ; 777 | 778 | simpleLeftHandExpression 779 | : simpleLeftHandExpression array 780 | { 781 | for (let i=0; i< $2.values.length; i++) { 782 | $1.selectors.push($2.values[i]); 783 | } 784 | setLines($1, @1, @2); 785 | } 786 | | IDENTIFIER %prec EMPTY 787 | { 788 | $$ = {type: "VARIABLE", name: $1 , selectors: []}; 789 | setLines($$, @1); 790 | } 791 | ; 792 | 793 | functionCall 794 | : IDENTIFIER '(' expressionList ')' 795 | { 796 | $$ = {type: "FUNCTIONCALL", name: $1, params: $3.expressions} 797 | setLines($$, @1, @4); 798 | } 799 | | IDENTIFIER '(' ')' 800 | { 801 | $$ = {type: "FUNCTIONCALL", name: $1, params: []} 802 | setLines($$, @1, @3); 803 | } 804 | ; 805 | 806 | expressionList 807 | : expressionList ',' expression 808 | { 809 | $1.expressions.push($3); 810 | setLines($$, @1, @3); 811 | } 812 | | expression %prec ',' 813 | { 814 | $$ = {type: "EXPRESSIONLST", expressions: [$1]}; 815 | setLines($$, @1); 816 | } 817 | ; 818 | 819 | rightArray 820 | : array %prec EMPTY 821 | { 822 | $$ = $1; 823 | } 824 | ; 825 | 826 | array 827 | : '[' expressionList ']' 828 | { 829 | $$ = { type: "ARRAY", values: $2.expressions}; 830 | setLines($$, @1, @3); 831 | } 832 | ; 833 | 834 | -------------------------------------------------------------------------------- /ports/c/builder.js: -------------------------------------------------------------------------------- 1 | const utils = require("../../src/utils"); 2 | const assert = require("assert"); 3 | const Scalar = require("ffjavascript").Scalar; 4 | const F1Field = require("ffjavascript").F1Field; 5 | const BigArray = require("../../src/bigarray"); 6 | 7 | function ref2src(c) { 8 | if ((c[0] == "R")||(c[0] == "RI")) { 9 | return c[1]; 10 | } else if (c[0] == "V") { 11 | return c[1].toString(); 12 | } else if (c[0] == "C") { 13 | return `(ctx->circuit->constants + ${c[1]})`; 14 | } else if (c[0] == "CC") { 15 | return "__cIdx"; 16 | } else { 17 | assert(false); 18 | } 19 | } 20 | 21 | class CodeBuilderC { 22 | constructor() { 23 | this.ops = []; 24 | } 25 | 26 | addComment(comment) { 27 | this.ops.push({op: "COMMENT", comment}); 28 | } 29 | 30 | addBlock(block) { 31 | this.ops.push({op: "BLOCK", block}); 32 | } 33 | 34 | calcOffset(dLabel, offsets) { 35 | this.ops.push({op: "CALCOFFSETS", dLabel, offsets}); 36 | } 37 | 38 | assign(dLabel, src, sOffset) { 39 | this.ops.push({op: "ASSIGN", dLabel, src, sOffset}); 40 | } 41 | 42 | getSubComponentOffset(dLabel, component, hash, hashLabel) { 43 | this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel}); 44 | } 45 | 46 | getSubComponentSizes(dLabel, component, hash, hashLabel) { 47 | this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel}); 48 | } 49 | 50 | getSignalOffset(dLabel, component, hash, hashLabel) { 51 | this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel}); 52 | } 53 | 54 | getSignalSizes(dLabel, component, hash, hashLabel) { 55 | this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel}); 56 | } 57 | 58 | setSignal(component, signal, value) { 59 | this.ops.push({op: "SETSIGNAL", component, signal, value}); 60 | } 61 | 62 | getSignal(dLabel, component, signal, n) { 63 | if (typeof(n) == "undefined") n=1; 64 | this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n}); 65 | } 66 | 67 | copyN(dLabel, offset, src, n) { 68 | this.ops.push({op: "COPYN", dLabel, offset, src, n}); 69 | } 70 | 71 | copyNRet(src, n) { 72 | this.ops.push({op: "COPYNRET", src, n}); 73 | } 74 | 75 | fieldOp(dLabel, fOp, params) { 76 | this.ops.push({op: "FOP", dLabel, fOp, params}); 77 | } 78 | 79 | ret() { 80 | this.ops.push({op: "RET"}); 81 | } 82 | 83 | addLoop(condLabel, body) { 84 | this.ops.push({op: "LOOP", condLabel, body}); 85 | } 86 | 87 | addIf(condLabel, thenCode, elseCode) { 88 | this.ops.push({op: "IF", condLabel, thenCode, elseCode}); 89 | } 90 | 91 | fnCall(fnName, retLabel, params) { 92 | this.ops.push({op: "FNCALL", fnName, retLabel, params}); 93 | } 94 | 95 | checkConstraint(a, b, strErr) { 96 | this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr}); 97 | } 98 | 99 | checkAssert(a, strErr) { 100 | this.ops.push({op: "CHECKASSERT", a, strErr}); 101 | } 102 | 103 | log(val) { 104 | this.ops.push({op: "LOG", val}); 105 | } 106 | 107 | concat(cb) { 108 | this.ops.push(...cb.ops); 109 | } 110 | 111 | hasCode() { 112 | for (let i=0; i { 122 | if ((o[0][0] == "V") && (o[1][0]== "V")) { 123 | rN += o[0][1]*o[1][1]; 124 | return; 125 | } 126 | let f=""; 127 | if (o[0][0] == "V") { 128 | if (o[0][1]==0) return; 129 | f += o[0][1]; 130 | } else if (o[0][0] == "RI") { 131 | if (o[0][1]==0) return; 132 | f += o[0][1]; 133 | } else if (o[0][0] == "R") { 134 | f += `Fr_toInt(${o[0][1]})`; 135 | } else { 136 | assert(false); 137 | } 138 | if (o[1][0] == "V") { 139 | if (o[1][1]==0) return; 140 | if (o[1][1]>1) { 141 | f += "*" + o[1][1]; 142 | } 143 | } else if (o[1][0] == "RS") { 144 | f += `*${o[1][1]}[${o[1][2]}]`; 145 | } else { 146 | assert(false); 147 | } 148 | if (S!="") S+= " + "; 149 | S += f; 150 | }); 151 | if (rN>0) { 152 | S = `${rN} + ${S}`; 153 | } 154 | return S; 155 | } 156 | 157 | build(code) { 158 | this.ops.forEach( (o) => { 159 | if (o.op == "COMMENT") { 160 | code.push(`/* ${o.comment} */`); 161 | } else if (o.op == "BLOCK") { 162 | const codeBlock=[]; 163 | o.block.build(codeBlock); 164 | code.push(utils.ident(codeBlock)); 165 | } else if (o.op == "CALCOFFSETS") { 166 | code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`); 167 | } else if (o.op == "ASSIGN") { 168 | const oS = ref2src(o.sOffset); 169 | if (oS != "0") { 170 | code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`); 171 | } else { 172 | code.push(`${o.dLabel} = ${ref2src(o.src)};`); 173 | } 174 | } else if (o.op == "GETSUBCOMPONENTOFFSET") { 175 | code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 176 | } else if (o.op == "GETSUBCOMPONENTSIZES") { 177 | code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 178 | } else if (o.op == "GETSIGNALOFFSET") { 179 | code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 180 | } else if (o.op == "GETSIGNALSIZES") { 181 | code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 182 | } else if (o.op == "SETSIGNAL") { 183 | code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`); 184 | } else if (o.op == "GETSIGNAL") { 185 | code.push(`ctx->multiGetSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel}, ${o.n});`); 186 | } else if (o.op == "COPYN") { 187 | const oS = ref2src(o.offset); 188 | const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel; 189 | code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`); 190 | } else if (o.op == "COPYNRET") { 191 | code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`); 192 | } else if (o.op == "RET") { 193 | code.push("goto returnFunc;"); 194 | } else if (o.op == "FOP") { 195 | let paramsS = ""; 196 | for (let i=0; i0) paramsS += ", "; 198 | paramsS += ref2src(o.params[i]); 199 | } 200 | code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`); 201 | } else if (o.op == "LOOP") { 202 | code.push(`while (Fr_isTrue(${o.condLabel})) {`); 203 | const body = []; 204 | o.body.build(body); 205 | code.push(utils.ident(body)); 206 | code.push("}"); 207 | } else if (o.op == "IF") { 208 | code.push(`if (Fr_isTrue(${o.condLabel})) {`); 209 | const thenCode = []; 210 | o.thenCode.build(thenCode); 211 | code.push(utils.ident(thenCode)); 212 | if (o.elseCode) { 213 | code.push("} else {"); 214 | const elseCode = []; 215 | o.elseCode.build(elseCode); 216 | code.push(utils.ident(elseCode)); 217 | } 218 | code.push("}"); 219 | } else if (o.op == "FNCALL") { 220 | code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`); 221 | } else if (o.op == "CHECKCONSTRAINT") { 222 | code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`); 223 | } else if (o.op == "CHECKASSERT") { 224 | code.push(`ctx->checkAssert(__cIdx, ${ref2src(o.a)}, "${o.strErr}");`); 225 | } else if (o.op == "LOG") { 226 | code.push(`ctx->log(${ref2src(o.val)});`); 227 | } 228 | }); 229 | } 230 | } 231 | 232 | class FunctionBuilderC { 233 | 234 | constructor(name, instanceDef, type) { 235 | this.name = name; 236 | this.instanceDef = instanceDef; 237 | this.type = type; // "COMPONENT" or "FUNCTIOM" 238 | this.definedFrElements = []; 239 | this.definedIntElements = []; 240 | this.definedSizeElements = []; 241 | this.definedPFrElements = []; 242 | this.initializedElements = []; 243 | this.initializedSignalOffset = []; 244 | this.initializedSignalSizes = []; 245 | 246 | } 247 | 248 | defineFrElements(dLabel, size) { 249 | this.definedFrElements.push({dLabel, size}); 250 | } 251 | 252 | defineIntElement(dLabel) { 253 | this.definedIntElements.push({dLabel}); 254 | } 255 | 256 | defineSizesElement(dLabel) { 257 | this.definedSizeElements.push({dLabel}); 258 | } 259 | 260 | definePFrElement(dLabel) { 261 | this.definedPFrElements.push({dLabel}); 262 | } 263 | 264 | initializeFrElement(dLabel, offset, idConstant) { 265 | this.initializedElements.push({dLabel, offset, idConstant}); 266 | } 267 | 268 | initializeSignalOffset(dLabel, component, hash, hashLabel) { 269 | this.initializedSignalOffset.push({dLabel, component, hash, hashLabel}); 270 | } 271 | 272 | initializeSignalSizes(dLabel, component, hash, hashLabel) { 273 | this.initializedSignalSizes.push({dLabel, component, hash, hashLabel}); 274 | } 275 | 276 | setParams(params) { 277 | this.params = params; 278 | } 279 | 280 | _buildHeader(code) { 281 | this.definedFrElements.forEach( (o) => { 282 | code.push(`FrElement ${o.dLabel}[${o.size}];`); 283 | }); 284 | this.definedIntElements.forEach( (o) => { 285 | code.push(`int ${o.dLabel};`); 286 | }); 287 | this.definedSizeElements.forEach( (o) => { 288 | code.push(`Circom_Sizes ${o.dLabel};`); 289 | }); 290 | this.definedPFrElements.forEach( (o) => { 291 | code.push(`PFrElement ${o.dLabel};`); 292 | }); 293 | this.initializedElements.forEach( (o) => { 294 | code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`); 295 | }); 296 | this.initializedSignalOffset.forEach( (o) => { 297 | code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 298 | }); 299 | this.initializedSignalSizes.forEach( (o) => { 300 | code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); 301 | }); 302 | } 303 | 304 | _buildFooter(code) { 305 | } 306 | 307 | newCodeBuilder() { 308 | return new CodeBuilderC(); 309 | } 310 | 311 | setBody(body) { 312 | this.body = body; 313 | } 314 | 315 | build(code) { 316 | code.push( 317 | "/*", 318 | this.instanceDef, 319 | "*/" 320 | ); 321 | 322 | if (this.type=="COMPONENT") { 323 | code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`); 324 | } else if (this.type=="FUNCTION") { 325 | let sParams = ""; 326 | for (let i=0;ifinished(__cIdx);"); 337 | } else if (this.type=="FUNCTION") { 338 | fnCode.push("returnFunc: ;"); 339 | } else { 340 | assert(false); 341 | } 342 | this._buildFooter(fnCode); 343 | 344 | code.push(utils.ident(fnCode)); 345 | code.push("}"); 346 | } 347 | 348 | } 349 | 350 | class BuilderC { 351 | constructor(p, verbose) { 352 | this.F = new F1Field(p); 353 | 354 | this.hashMaps={}; 355 | this.componentEntriesTables=new BigArray(); 356 | this.sizes ={}; 357 | this.constants = []; 358 | this.functions = []; 359 | this.components = new BigArray(); 360 | this.usedConstants = {}; 361 | this.verbose = verbose; 362 | 363 | 364 | this.sizePointers = {}; 365 | this.hashMapPointers = {}; 366 | this.functionIdx = {}; 367 | this.nCets = 0; 368 | } 369 | 370 | setHeader(header) { 371 | this.header=header; 372 | } 373 | 374 | // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements. 375 | addHashMap(name, hm) { 376 | this.hashMaps[name] = hm; 377 | } 378 | 379 | addComponentEntriesTable(name, cet, idComponent) { 380 | this.componentEntriesTables[idComponent] = { 381 | name: name, 382 | cet: cet 383 | }; 384 | } 385 | 386 | addSizes(name, accSizes) { 387 | this.sizes[name] = accSizes; 388 | } 389 | 390 | addConstant(c) { 391 | c = this.F.e(c); 392 | const cS = c.toString(); 393 | if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS]; 394 | this.constants.push(c); 395 | this.usedConstants[cS] = this.constants.length - 1; 396 | return this.constants.length - 1; 397 | } 398 | 399 | addFunction(fnBuilder) { 400 | this.functions.push(fnBuilder); 401 | } 402 | 403 | addComponent(component) { 404 | this.components.push(component); 405 | } 406 | 407 | setMapIsInput(map) { 408 | this.mapIsInput = map; 409 | } 410 | 411 | setWit2Sig(wit2sig) { 412 | this.wit2sig = wit2sig; 413 | } 414 | 415 | 416 | newComponentFunctionBuilder(name, instanceDef) { 417 | return new FunctionBuilderC(name, instanceDef, "COMPONENT"); 418 | } 419 | 420 | newFunctionBuilder(name, instanceDef) { 421 | return new FunctionBuilderC(name, instanceDef, "FUNCTION"); 422 | } 423 | 424 | 425 | // Body functions 426 | 427 | _buildHeader(code) { 428 | code.push( 429 | "#include \"circom.hpp\"", 430 | "#include \"calcwit.hpp\"", 431 | `#define NSignals ${this.header.NSignals}`, 432 | `#define NComponents ${this.header.NComponents}`, 433 | `#define NOutputs ${this.header.NOutputs}`, 434 | `#define NInputs ${this.header.NInputs}`, 435 | `#define NVars ${this.header.NVars}`, 436 | `#define NPublic ${this.header.NPublic}`, 437 | `#define __P__ "${this.header.P.toString()}"`, 438 | "" 439 | ); 440 | } 441 | 442 | async _buildHashMaps(fdData) { 443 | 444 | while (fdData.pos % 8) fdData.pos++; 445 | this.pHashMaps = fdData.pos; 446 | 447 | const buff = new Uint8Array(256*12); 448 | const buffV = new DataView(buff.buffer); 449 | for (let hmName in this.hashMaps ) { 450 | 451 | while (fdData.pos % 8) fdData.pos++; 452 | this.hashMapPointers[hmName] = fdData.pos; 453 | const hm = this.hashMaps[hmName]; 454 | 455 | for (let i=0; i<256; i++) { 456 | buffV.setUint32(i*12, hm[i] ? parseInt( hm[i][0].slice(8), 16 ) : 0, true); 457 | buffV.setUint32(i*12+4, hm[i] ? parseInt( hm[i][0].slice(0,8), 16 ) : 0, true); 458 | buffV.setUint32(i*12+8, hm[i] ? hm[i][1] : 0, true); 459 | } 460 | 461 | await fdData.write(buff); 462 | } 463 | 464 | } 465 | 466 | async _buildComponentEntriesTables(fdData) { 467 | 468 | while (fdData.pos % 8) fdData.pos++; 469 | this.pCets = fdData.pos; 470 | for (let i=0; i< this.componentEntriesTables.length; i++) { 471 | if ((this.verbose)&&(i%100000 ==0)) console.log(`_buildComponentEntriesTables ${i}/${this.componentEntriesTables.length}`); 472 | const cet = this.componentEntriesTables[i].cet; 473 | 474 | this.components[i].entryTablePointer = fdData.pos; 475 | const buff = new Uint8Array(16*cet.length); 476 | const buffV = new DataView(buff.buffer); 477 | 478 | for (let j=0; j Signal, 1->Component 482 | this.nCets ++; 483 | } 484 | 485 | await fdData.write(buff); 486 | 487 | } 488 | } 489 | 490 | async _buildSizes(fdData) { 491 | for (let sName in this.sizes) { 492 | const accSizes = this.sizes[sName]; 493 | 494 | while (fdData.pos % 8) fdData.pos++; 495 | this.sizePointers[sName] = fdData.pos; 496 | 497 | const buff = new Uint8Array(4*accSizes.length); 498 | const buffV = new DataView(buff.buffer); 499 | for (let i=0; i=0) { 571 | buffV.setUint32(p, arr[idx], true); 572 | } else { 573 | buffV.setUint32(p, 0, true); 574 | } 575 | p+= 4; 576 | } 577 | } 578 | 579 | function toMontgomery(a) { 580 | return self.F.mul(a, self.F.R); 581 | } 582 | 583 | } 584 | 585 | } 586 | 587 | _buildFunctions(code) { 588 | const listedFunctions = []; 589 | for (let i=0; i0 ? " ," : " "; 602 | code.push(`${sep}${this.functions[listedFunctions[i]].name}`); 603 | } 604 | code.push("};"); 605 | } 606 | 607 | 608 | async _buildComponents(fdData) { 609 | 610 | const buff = new Uint8Array(32); 611 | const buffV = new DataView(buff.buffer); 612 | 613 | while (fdData.pos % 8) fdData.pos++; 614 | this.pComponents = fdData.pos; 615 | 616 | for (let i=0; i ${v}`); 177 | } 178 | return lines.join("\n"); 179 | } 180 | 181 | async checkConstraints(witness) { 182 | const self = this; 183 | if (!self.constraints) await self.loadConstraints(); 184 | for (let i=0; i ${v}`); 124 | } 125 | return lines.join("\n"); 126 | } 127 | 128 | async checkConstraints(witness) { 129 | const self = this; 130 | if (!self.constraints) await self.loadConstraints(); 131 | for (let i=0; i= this.length) this.length = idx+1; 49 | return true; 50 | } 51 | getKeys() { 52 | const newA = new BigArray(); 53 | for (let i=0; i. 18 | */ 19 | 20 | const assert = require("assert"); 21 | const utils = require("./utils"); 22 | const gen = require("./gencode").gen; 23 | const createRefs = require("./gencode").createRefs; 24 | const BigArray = require("./bigarray"); 25 | 26 | module.exports = build; 27 | 28 | 29 | function build(ctx) { 30 | ctx.definedFunctions = {}; 31 | ctx.functionCodes = []; 32 | ctx.buildFunction = buildFunction; 33 | ctx.conditionalCodeHeader = ""; 34 | ctx.codes_sizes = []; 35 | ctx.definedSizes = {}; 36 | ctx.addSizes = addSizes; 37 | ctx.addConstant = addConstant; 38 | ctx.addConstant(ctx.F.zero); 39 | ctx.addConstant(ctx.F.one); 40 | 41 | if (ctx.verbose) console.log("buildHeader..."); 42 | buildHeader(ctx); 43 | if (ctx.verbose) console.log("buildEntryTables..."); 44 | buildEntryTables(ctx); 45 | ctx.globalNames = ctx.uniqueNames; 46 | 47 | if (ctx.verbose) console.log("buildCode..."); 48 | buildCode(ctx); 49 | 50 | if (ctx.verbose) console.log("buildComponentsArray..."); 51 | buildComponentsArray(ctx); 52 | 53 | if (ctx.verbose) console.log("buildMapIsInput..."); 54 | buildMapIsInput(ctx); 55 | 56 | if (ctx.verbose) console.log("buildWit2Sig..."); 57 | buildWit2Sig(ctx); 58 | 59 | } 60 | 61 | function buildEntryTables(ctx) { 62 | 63 | const definedHashMaps = {}; 64 | for (let i=0; i ((a>b) ? 1 : -1)); 94 | const h = utils.fnvHash(keys.join(",")); 95 | if (definedHashMaps[h]) return definedHashMaps[h]; 96 | definedHashMaps[h] = {}; 97 | definedHashMaps[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template); 98 | definedHashMaps[h].htMap = []; 99 | const t = []; 100 | for (let i=0; i=0) continue; // If has an alias, continue.. 239 | assert(typeof outIdx != "undefined", `Signal ${i} does not have index`); 240 | if (outIdx>=NVars) continue; // Is a constant or a discarded variable 241 | if (typeof arr[ctx.signals[i].id] == "undefined") { 242 | arr[outIdx] = i; 243 | } 244 | } 245 | 246 | ctx.builder.setWit2Sig(arr); 247 | } 248 | 249 | 250 | 251 | function addSizes(_sizes) { 252 | const sizes = _sizes || []; 253 | let name = "sizes"; 254 | for (let i=0; i0) code += ","; 270 | code += accSizes[i]; 271 | } 272 | code += "};\n"; 273 | this.codes_sizes.push(code); 274 | 275 | return labelName; 276 | } 277 | 278 | function addConstant(c) { 279 | return this.builder.addConstant(c); 280 | } 281 | 282 | function buildFunction(name, paramValues) { 283 | const ctx = this; 284 | const {h, instanceDef} = hashFunctionCall(ctx, name, paramValues); 285 | 286 | if (ctx.definedFunctions[h]) return ctx.definedFunctions[h]; 287 | 288 | const res = { 289 | fnName: `${name}_${h}` 290 | }; 291 | 292 | const oldRefs = ctx.refs; 293 | const oldConditionalCode = ctx.conditionalCode; 294 | const oldCodeBuilder = ctx.codeBuilder; 295 | const oldFnBuilder = ctx.fnBuilder; 296 | 297 | const oldUniqueNames = ctx.uniqueNames; 298 | const oldFileName = ctx.fileName; 299 | const oldFilePath = ctx.oldFilePath; 300 | const oldReturnSizes = ctx.returnSizes; 301 | const oldReturnValue = ctx.returnValue; 302 | 303 | 304 | ctx.scopes = [{}]; 305 | ctx.refs = []; 306 | ctx.conditionalCode = false; 307 | ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef, ctx.functions[name].params); 308 | ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); 309 | ctx.uniqueNames = Object.assign({},ctx.globalNames); 310 | ctx.returnValue = null; 311 | ctx.returnSizes = null; 312 | ctx.fileName = ctx.functions[name].fileName; 313 | ctx.filePath = ctx.functions[name].filePath; 314 | 315 | let paramLabels = []; 316 | 317 | for (let i=0; i { 391 | if (utils.isDefined(ctx.signals[offset].v)) { 392 | constParams.push(prefix + "=" + ctx.F.e(ctx.signals[offset].v)); 393 | } 394 | }); 395 | } 396 | } 397 | 398 | let instanceDef = ctx.components[cIdx].template; 399 | if (constParams.length>0) { 400 | instanceDef += "\n"; 401 | constParams.sort(); 402 | instanceDef += constParams.join("\n"); 403 | } 404 | const h = utils.fnvHash(instanceDef); 405 | return {h, instanceDef}; 406 | 407 | function travelSizes(prefix, offset, sizes, fn) { 408 | if (sizes.length == 0) { 409 | fn(prefix, offset); 410 | return 1; 411 | } else { 412 | let o = offset; 413 | for (let i=0; i0) S+=","; 445 | S+=value2str(F, v[i]); 446 | } 447 | S+="]"; 448 | return S; 449 | } else { 450 | return F.toString(F.e(v)); 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /src/buildsyms.js: -------------------------------------------------------------------------------- 1 | const Readable = require("stream").Readable; 2 | 3 | module.exports = function buildSyms(ctx) { 4 | const rs = Readable(); 5 | 6 | let it = new ComponentIt(ctx, 0, "main"); 7 | 8 | let counter = 0; 9 | 10 | rs._read = function() { 11 | const actual = it.current(); 12 | if (actual == null ) { 13 | rs.push(null); 14 | return; 15 | } 16 | 17 | 18 | let s=actual.offset; 19 | while (ctx.signals[s].e >= 0) s = ctx.signals[s].e; 20 | let wId = ctx.signals[s].id; 21 | if (typeof(wId) == "undefined") wId=-1; 22 | const line = `${actual.offset},${wId},${actual.cIdx},${actual.name}\n`; 23 | if ((ctx.verbose)&&(counter%10000 == 0)) { 24 | console.log("Symbols saved: "+counter); 25 | setImmediate(function() { 26 | rs.push(line); 27 | }); 28 | } else { 29 | rs.push(line); 30 | } 31 | it.next(); 32 | counter ++; 33 | }; 34 | 35 | return rs; 36 | }; 37 | 38 | 39 | 40 | class SignalIt { 41 | constructor (ctx, offset, prefix, cIdx) { 42 | this.ctx = ctx; 43 | this.offset = offset; 44 | this.prefix = prefix; 45 | this.cur = 0; 46 | this.cIdx = cIdx; 47 | } 48 | 49 | next() { 50 | this.cur = 1; 51 | 52 | return this.current(); 53 | } 54 | 55 | current() { 56 | if (this.cur == 0) { 57 | return {offset: this.offset, name: this.prefix, cIdx: this.cIdx}; 58 | } 59 | } 60 | } 61 | 62 | class ArrayIt { 63 | constructor (ctx, type, sizes, offset, prefix, cIdx) { 64 | if (sizes.length == 0) { 65 | if (type == "S") { 66 | return new SignalIt(ctx, offset, prefix, cIdx); 67 | } else { 68 | return new ComponentIt(ctx, offset, prefix); 69 | } 70 | } 71 | 72 | this.ctx = ctx; 73 | this.type = type; 74 | this.sizes = sizes; 75 | this.offset = offset; 76 | this.prefix = prefix; 77 | this.cIdx = cIdx; 78 | 79 | 80 | 81 | this.subIt = null; 82 | this.cur = 0; 83 | 84 | this.subArrSize = 1; 85 | 86 | for (let i=1; i. 18 | */ 19 | 20 | const Scalar = require("ffjavascript").Scalar; 21 | const sONE = 0; 22 | const build = require("./build"); 23 | const BuilderC = require("../ports/c/builder.js"); 24 | const BuilderWasm = require("../ports/wasm/builder.js"); 25 | const constructionPhase = require("./construction_phase"); 26 | const Ctx = require("./ctx"); 27 | const utils = require("./utils"); 28 | const buildR1cs = require("./r1csfile").buildR1cs; 29 | const BigArray = require("./bigarray"); 30 | const buildSyms = require("./buildsyms"); 31 | const {performance} = require("perf_hooks"); 32 | const fastFile = require("fastfile"); 33 | 34 | module.exports = compile; 35 | const measures = {}; 36 | 37 | function ms2String(v) { 38 | v = Math.floor(v); 39 | const ms = v % 1000; 40 | v = Math.floor(v/1000); 41 | const secs = v % 60; 42 | v = Math.floor(v/60); 43 | const mins = v % 60; 44 | v = Math.floor(v/60); 45 | const hours = v % 24; 46 | const days = Math.floor(v/24); 47 | let S = ""; 48 | if (days) S = S + days + "D "; 49 | if ((S!="")||(hours)) S = S + hours.toString().padStart(2, "0") + ":"; 50 | if ((S!="")||(mins)) S = S + mins.toString().padStart(2, "0") + ":"; 51 | if ((S!="")||(secs)) S = S + secs.toString().padStart(2, "0"); 52 | S+="."; 53 | S = S + ms.toString().padStart(3, "0"); 54 | return S; 55 | } 56 | 57 | async function compile(srcFile, options) { 58 | options.prime = options.prime || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 59 | if (!options) { 60 | options = {}; 61 | } 62 | if (typeof options.reduceConstraints === "undefined") { 63 | options.reduceConstraints = true; 64 | } 65 | const ctx = new Ctx(options.prime); 66 | ctx.verbose= options.verbose || false; 67 | ctx.mainComponent = options.mainComponent || "main"; 68 | ctx.newThreadTemplates = options.newThreadTemplates; 69 | 70 | measures.constructionPhase = -performance.now(); 71 | constructionPhase(ctx, srcFile); 72 | measures.constructionPhase += performance.now(); 73 | 74 | if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length); 75 | if (ctx.verbose) console.log("NSignals Before: "+ctx.signals.length); 76 | 77 | if (ctx.error) { 78 | throw(ctx.error); 79 | } 80 | 81 | if (ctx.getComponentIdx(ctx.mainComponent)<0) { 82 | throw new Error("A main component must be defined"); 83 | } 84 | 85 | 86 | if (ctx.verbose) console.log("Reduce Constants"); 87 | measures.reduceConstants = -performance.now(); 88 | reduceConstants(ctx); 89 | measures.reduceConstants += performance.now(); 90 | 91 | if (options.reduceConstraints) { 92 | 93 | if (ctx.verbose) console.log("Reduce Constraints"); 94 | // Repeat while reductions are performed 95 | /* 96 | let oldNConstrains = -1; 97 | while (ctx.constraints.length != oldNConstrains) { 98 | if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length); 99 | oldNConstrains = ctx.constraints.length; 100 | reduceConstrains(ctx); 101 | } 102 | */ 103 | measures.reduceConstraints = -performance.now(); 104 | await reduceConstrains(ctx); 105 | measures.reduceConstraints += performance.now(); 106 | 107 | } 108 | if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length); 109 | 110 | if (ctx.verbose) console.log("Classify Signals"); 111 | measures.classifySignals = -performance.now(); 112 | classifySignals(ctx); 113 | measures.classifySignals += performance.now(); 114 | 115 | measures.generateWitnessNames = -performance.now(); 116 | generateWitnessNames(ctx); 117 | measures.generateWitnessNames += performance.now(); 118 | 119 | if (ctx.error) { 120 | throw(ctx.error); 121 | } 122 | 123 | if (options.r1csFileName) { 124 | measures.generateR1cs = -performance.now(); 125 | await buildR1cs(ctx, options.r1csFileName); 126 | measures.generateR1cs += performance.now(); 127 | } 128 | 129 | if (ctx.error) throw(ctx.error); 130 | 131 | delete ctx.constraints; // Liberate memory. 132 | 133 | if (options.cSourceFileName) { 134 | options.cSourceFile = await fastFile.createOverride(options.cSourceFileName); 135 | } 136 | 137 | if (options.dataFileName) { 138 | options.dataFile = await fastFile.createOverride(options.dataFileName); 139 | } 140 | 141 | if (options.cSourceFile) { 142 | if (ctx.verbose) console.log("Generating c..."); 143 | measures.generateC = -performance.now(); 144 | ctx.builder = new BuilderC(options.prime, ctx.verbose); 145 | build(ctx); 146 | await ctx.builder.build(options.cSourceFile, options.dataFile); 147 | measures.generateC += performance.now(); 148 | } 149 | 150 | if (ctx.error) throw(ctx.error); 151 | 152 | if (options.wasmFileName) { 153 | options.wasmFile = await fastFile.createOverride(options.wasmFileName); 154 | } 155 | 156 | if (options.watFileName) { 157 | options.watFile = await fastFile.createOverride(options.watFileName); 158 | } 159 | 160 | if ((options.wasmFile)||(options.watFile)) { 161 | if (ctx.verbose) console.log("Generating wasm..."); 162 | measures.generateWasm = -performance.now(); 163 | ctx.builder = new BuilderWasm(options.prime); 164 | build(ctx); 165 | if (options.wasmFile) { 166 | await ctx.builder.build(options.wasmFile, "wasm"); 167 | } 168 | if (options.watFile) { 169 | await ctx.builder.build(options.watFile, "wat"); 170 | } 171 | measures.generateWasm += performance.now(); 172 | } 173 | 174 | // const mainCode = gen(ctx,ast); 175 | if (ctx.error) throw(ctx.error); 176 | 177 | if (options.symWriteStream) { 178 | measures.generateSyms = -performance.now(); 179 | const rdStream = buildSyms(ctx); 180 | rdStream.pipe(options.symWriteStream); 181 | measures.generateSyms += performance.now(); 182 | 183 | await new Promise(fulfill => options.symWriteStream.on("finish", fulfill)); 184 | } 185 | 186 | // const def = buildCircuitDef(ctx, mainCode); 187 | 188 | if (ctx.verbose) { 189 | for (let [mStr, mValue] of Object.entries(measures)) { 190 | console.log(mStr + ": " + ms2String(mValue)); 191 | } 192 | } 193 | 194 | if (options.cSourceFile) await options.cSourceFile.close(); 195 | if (options.dataFile) await options.dataFile.close(); 196 | if (options.wasmFile) await options.wasmFile.close(); 197 | if (options.watFile) await options.watFile.close(); 198 | } 199 | 200 | 201 | 202 | function classifySignals(ctx) { 203 | 204 | const ERROR = 0xFFFF; 205 | 206 | function priorize(t1, t2) { 207 | if ((t1 == ERROR) || (t2==ERROR)) return ERROR; 208 | if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE; 209 | if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT; 210 | if ((t1 == ctx.stPUBINPUT) || (t2 == ctx.stPUBINPUT)) return ctx.stPUBINPUT; 211 | if ((t1 == ctx.stPRVINPUT) || (t2 == ctx.stPRVINPUT)) return ctx.stPRVINPUT; 212 | if ((t1 == ctx.stINTERNAL) || (t2 == ctx.stINTERNAL)) return ctx.stINTERNAL; 213 | if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT; 214 | if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED; 215 | if (t1!=t2) return ERROR; 216 | return t1; 217 | } 218 | 219 | for (let i=0; i=0) { 255 | lSignal = ctx.signals[lSignal.e]; 256 | } else { 257 | end=true; 258 | } 259 | } 260 | if (tAll == ERROR) { 261 | throw new Error("Incompatible types in signal: " + s); 262 | } 263 | lSignal.c = tAll; 264 | } 265 | } 266 | 267 | 268 | function generateWitnessNames(ctx) { 269 | const totals = {}; 270 | totals[ctx.stONE] = 0; 271 | totals[ctx.stOUTPUT] = 0; 272 | totals[ctx.stPUBINPUT] = 0; 273 | totals[ctx.stPRVINPUT] = 0; 274 | totals[ctx.stINTERNAL] = 0; 275 | totals[ctx.stDISCARDED] = 0; 276 | totals[ctx.stCONSTANT] = 0; 277 | const ids = {}; 278 | 279 | // First classify the signals 280 | for (let s=0; s=0) lSignal = ctx.signals[lSignal.e]; 287 | 288 | if (!( lSignal.o & ctx.COUNTED) ) { 289 | lSignal.o |= ctx.COUNTED; 290 | totals[lSignal.c] ++; 291 | } 292 | } 293 | 294 | ids[ctx.stONE] = 0; 295 | ids[ctx.stOUTPUT] = 1; 296 | ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT]; 297 | ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT]; 298 | ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT]; 299 | ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL]; 300 | ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED]; 301 | const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT]; 302 | 303 | for (let s=0; s=0) { 310 | lSignal = ctx.signals[lSignal.e]; 311 | } 312 | if ( typeof(lSignal.id) === "undefined" ) { 313 | lSignal.id = ids[lSignal.c] ++; 314 | } 315 | 316 | signal.id = lSignal.id; 317 | } 318 | 319 | ctx.totals = totals; 320 | } 321 | 322 | function reduceConstants(ctx) { 323 | const newConstraints = new BigArray(); 324 | for (let i=0; i0) { 375 | if (possibleConstraints.length>1<<20) { 376 | nextPossibleConstraints = new BigArray(); 377 | } else { 378 | nextPossibleConstraints = {}; 379 | } 380 | removedSignals = {}; 381 | nRemoved = 0; 382 | lIdx = {}; 383 | for (let i=0;i5000000) { 394 | nextPossibleConstraints[possibleConstraints[i]] = true; 395 | continue; 396 | } 397 | 398 | // Swap a and b if b has more variables. 399 | if (Object.keys(c.b).length > Object.keys(c.a).length) { 400 | const aux = c.a; 401 | c.a=c.b; 402 | c.b=aux; 403 | } 404 | 405 | // Mov to C if possible. 406 | if (isConstant(c.a)) { 407 | const ct = {t: "N", v: c.a.coefs[sONE]}; 408 | c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c); 409 | c.a = { t: "LC", coefs: {} }; 410 | c.b = { t: "LC", coefs: {} }; 411 | } 412 | if (isConstant(c.b)) { 413 | const ct = {t: "N", v: c.b.coefs[sONE]}; 414 | c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c); 415 | c.a = { t: "LC", coefs: {} }; 416 | c.b = { t: "LC", coefs: {} }; 417 | } 418 | 419 | if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) { 420 | const freeC = substituteRemoved(c.c); 421 | const isolatedSignal = getFirstInternalSignal(ctx, freeC); 422 | if (isolatedSignal) { 423 | removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal); 424 | if (lIdx[isolatedSignal]) { 425 | const sigs = Object.keys(lIdx[isolatedSignal]); 426 | 427 | for (let k=0; k=0) { 483 | lSignal = ctx.signals[lSignal.e]; 484 | } 485 | 486 | sig2constraint[s] = null; 487 | } 488 | 489 | /* 490 | possibleConstraints = new BigArray(); 491 | // Reverse 492 | for (let i=0; i1) { 29 | return [o, o+l]; 30 | } else { 31 | return o; 32 | } 33 | } 34 | 35 | addComponent(name, sizes) { 36 | const l = this._allocElement(name, sizes, "C"); 37 | const o = this.ctx.nComponents; 38 | this.o[name].offset = o; 39 | this.ctx.nComponents += l; 40 | if (l>1) { 41 | return [o, o+l]; 42 | } else { 43 | return o; 44 | } 45 | } 46 | 47 | _getElement(name, _sels, type) { 48 | const sels = _sels || []; 49 | const s = this.o[name]; 50 | if (!s) return -1; 51 | if (s.type != type) return -1; 52 | if (sels.length > s.sizes.length) return -1; 53 | let l=1; 54 | for (let i = s.sizes.length-1; i>sels.length; i--) { 55 | l = l*s.sizes[i]; 56 | } 57 | let o =0; 58 | let p=1; 59 | for (let i=sels.length-1; i>=0; i--) { 60 | if (sels[i] > s.sizes[i]) return -1; // Out of range 61 | if (sels[i] < 0) return -1; // Out of range 62 | o += p*sels[i]; 63 | p *= s.sizes[i]; 64 | } 65 | if (l>1) { 66 | return [s.offset + o, s.offset + o + l]; 67 | } else { 68 | return s.offset + o; 69 | } 70 | } 71 | 72 | getSignalIdx(name, sels) { 73 | return this._getElement(name, sels, "S"); 74 | } 75 | 76 | getComponentIdx(name, sels) { 77 | return this._getElement(name, sels, "C"); 78 | } 79 | 80 | getSizes(name) { 81 | return this.o[name].sels; 82 | } 83 | 84 | } 85 | 86 | module.exports = class Ctx { 87 | 88 | constructor(p) { 89 | 90 | this.F = new F1Field(p); 91 | 92 | this.stONE = 1; 93 | this.stOUTPUT = 2; 94 | this.stPUBINPUT = 3; 95 | this.stPRVINPUT = 4; 96 | this.stINTERNAL = 5; 97 | this.stDISCARDED = 6; 98 | this.stCONSTANT = 7; 99 | 100 | this.IN = 0x01; 101 | this.OUT = 0x02; 102 | this.PRV = 0x04; 103 | this.ONE = 0x08; 104 | this.MAIN = 0x10; 105 | this.COUNTED = 0x20; 106 | 107 | this.scopes = [{}]; 108 | this.signals = new BigArray(); 109 | 110 | this.currentComponent= -1; 111 | this.constraints= new BigArray(); 112 | this.components= new BigArray(); 113 | this.templates= {}; 114 | this.functions= {}; 115 | this.functionParams= {}; 116 | this.nSignals = 0; 117 | this.nComponents =0; 118 | this.names = new TableName(this); 119 | this.main=false; 120 | this.error = null; 121 | this.warnings = []; 122 | 123 | const oneIdx = this.addSignal("one"); 124 | this.signals[oneIdx] = { 125 | v: this.F.one, 126 | o: this.ONE, 127 | e: -1, 128 | }; 129 | 130 | this.uniqueNames = {}; 131 | } 132 | 133 | addSignal(name, sizes) { 134 | if (this.currentComponent>=0) { 135 | return this.components[this.currentComponent].names.addSignal(name, sizes); 136 | } else { 137 | return this.names.addSignal(name, sizes); 138 | } 139 | } 140 | 141 | addComponent(name, sizes) { 142 | if (this.currentComponent>=0) { 143 | return this.components[this.currentComponent].names.addComponent(name, sizes); 144 | } else { 145 | return this.names.addComponent(name, sizes); 146 | } 147 | } 148 | 149 | getSignalIdx(name, sels) { 150 | if (this.currentComponent>=0) { 151 | return this.components[this.currentComponent].names.getSignalIdx(name, sels); 152 | } else { 153 | return this.names.getSignalIdx(name, sels); 154 | } 155 | } 156 | 157 | getComponentIdx(name, sels) { 158 | if (this.currentComponent>=0) { 159 | return this.components[this.currentComponent].names.getComponentIdx(name, sels); 160 | } else { 161 | return this.names.getComponentIdx(name, sels); 162 | } 163 | } 164 | 165 | getSizes(name) { 166 | if (this.currentComponent>=0) { 167 | return this.components[this.currentComponent].names.getSizes(name); 168 | } else { 169 | return this.names.getSizes(name); 170 | } 171 | } 172 | 173 | newTableName() { 174 | return new TableName(this); 175 | } 176 | 177 | _buildErr(ast, errStr) { 178 | if (typeof ast == "string") { 179 | ast = null; 180 | errStr = ast; 181 | } 182 | if (ast) { 183 | return { 184 | pos: { 185 | first_line: ast.first_line, 186 | first_column: ast.first_column, 187 | last_line: ast.last_line, 188 | last_column: ast.last_column 189 | }, 190 | errStr: errStr, 191 | ast: ast, 192 | message: errStr, 193 | errFile: this.fileName 194 | }; 195 | } else { 196 | return { 197 | errStr: errStr, 198 | message: errStr 199 | }; 200 | } 201 | } 202 | 203 | throwError(ast, errStr) { 204 | const err = this._buildErr(ast, errStr); 205 | this.error = err; 206 | } 207 | 208 | logWarning(ast, errStr) { 209 | const w = this._buildErr(ast, errStr); 210 | this.warnings.push(w); 211 | } 212 | 213 | getUniqueName(suggestedName) { 214 | if (!suggestedName) { 215 | suggestedName = "_tmp"; 216 | } 217 | 218 | if (typeof(this.uniqueNames[suggestedName]) == "undefined") { 219 | this.uniqueNames[suggestedName] = 1; 220 | return suggestedName; 221 | } else { 222 | const name = suggestedName + "_" + this.uniqueNames[suggestedName]; 223 | this.uniqueNames[suggestedName]++; 224 | return name; 225 | } 226 | } 227 | 228 | }; 229 | -------------------------------------------------------------------------------- /src/iterateast.js: -------------------------------------------------------------------------------- 1 | 2 | const assert = require("assert"); 3 | 4 | module.exports = iterateAST; 5 | 6 | 7 | function iterateAST(ast, fn, _pfx) { 8 | if (!ast) return; 9 | 10 | const pfx = _pfx || ""; 11 | let itPfx = 0; 12 | 13 | function getPfx() { 14 | res = pfx+"."+itPfx; 15 | itPfx ++; 16 | return res; 17 | } 18 | 19 | 20 | let res = fn(ast, pfx); 21 | if (res) return res; 22 | function iterate(arr) { 23 | if (arr) { 24 | for (let i=0; i Invalid AST iteration: " + ast.type); 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/lcalgebra.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | /* 20 | 21 | // Number 22 | /////////////// 23 | N: a 24 | 25 | { 26 | t: "N", 27 | v: bigInt(a) 28 | } 29 | 30 | // Signal 31 | /////////////// 32 | { 33 | t: "S", 34 | sIdx: sIdx 35 | } 36 | 37 | // Linear Convination 38 | ////////////////// 39 | LC: c1*s1 + c2*s2 + c3*s3 40 | { 41 | t: "LC", 42 | coefs: { 43 | s1: bigInt(c1), 44 | s2: bigInt(c2), 45 | s3: bigInt(c3) 46 | } 47 | } 48 | 49 | // Quadratic Expression 50 | ////////////////// 51 | QEX: a*b + c WHERE a,b,c are LC 52 | { 53 | t: "QEX" 54 | a: { t: "LC", coefs: {...} }, 55 | b: { t: "LC", coefs: {...} }, 56 | c: { t: "LC", coefs: {...} } 57 | } 58 | 59 | NQ: Non quadratic expression 60 | { 61 | t: "NQ" 62 | } 63 | */ 64 | 65 | /* 66 | + N LC QEX NQ 67 | N N LC QEX NQ 68 | LC LC LC QEX NQ 69 | QEX QEX QEX NQ NQ 70 | NQ NQ NQ NQ NQ 71 | 72 | * N LC QEX NQ 73 | N N LC QEX NQ 74 | LC LC QEX NQ NQ 75 | QEX QEX NQ NQ NQ 76 | NQ NQ NQ NQ NQ 77 | */ 78 | 79 | const utils = require("./utils"); 80 | const sONE = 0; 81 | 82 | class LCAlgebra { 83 | constructor (aField) { 84 | const self = this; 85 | this.F= aField; 86 | [ 87 | ["idiv",2], 88 | ["mod",2], 89 | ["band",2], 90 | ["bor",2], 91 | ["bxor",2], 92 | ["bnot",2], 93 | ["land",2], 94 | ["lor",2], 95 | ["lnot",2], 96 | ["shl",2], 97 | ["shr",2], 98 | ["lt",2, true], 99 | ["leq",2, true], 100 | ["eq",2, true], 101 | ["neq",2, true], 102 | ["geq",2, true], 103 | ["gt",2, true] 104 | ].forEach( (op) => { 105 | self._genNQOp(op[0], op[1], op[2]); 106 | }); 107 | } 108 | 109 | _genNQOp(op, nOps, adjustBool) { 110 | const self=this; 111 | self[op] = function() { 112 | const operands = []; 113 | for (let i=0; i=0) sIdx = ctx.signals[sIdx].e; 473 | } 474 | S = S + "[" + sIdx + "]"; 475 | } 476 | } 477 | if (S=="") return "0"; else return S; 478 | } else if (a.t == "QEX") { 479 | return "( "+ 480 | this.toString(a.a, ctx)+" ) * ( "+ 481 | this.toString(a.b, ctx)+" ) + " + 482 | this.toString(a.c, ctx); 483 | } else { 484 | return "NQ"; 485 | } 486 | } 487 | 488 | evaluate(ctx, n) { 489 | if (n.t == "N") { 490 | return n.v; 491 | } else if (n.t == "SIGNAL") { 492 | return getSignalValue(ctx, n.sIdx); 493 | } else if (n.t == "LC") { 494 | let v= this.F.zero; 495 | for (let k in n.coefs) { 496 | const s = getSignalValue(ctx, k); 497 | if (s === null) return null; 498 | v = this.F.add(v, this.F.mul( n.coefs[k], s)); 499 | } 500 | return v; 501 | } else if (n.t == "QEX") { 502 | const a = this.evaluate(ctx, n.a); 503 | if (a === null) return null; 504 | const b = this.evaluate(ctx, n.b); 505 | if (b === null) return null; 506 | const c = this.evaluate(ctx, n.c); 507 | if (c === null) return null; 508 | 509 | return this.F.add(this.F.mul(a,b), c); 510 | } else { 511 | return null; 512 | } 513 | 514 | 515 | function getSignalValue(ctx, sIdx) { 516 | let s = ctx.signals[sIdx]; 517 | while (s.e>=0) s = ctx.signals[s.e]; 518 | if (utils.isDefined(s.v)) return s.v; 519 | return null; 520 | } 521 | 522 | } 523 | 524 | 525 | canonize(ctx, a) { 526 | if (a.t == "LC") { 527 | const res = this._clone(a); 528 | for (let k in a.coefs) { 529 | let s = k; 530 | while (ctx.signals[s].e>=0) s= ctx.signals[s].e; 531 | if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) { 532 | const v = this.F.mul(res.coefs[k], ctx.signals[s].v); 533 | if (!utils.isDefined(res.coefs[sONE])) { 534 | res.coefs[sONE]=v; 535 | } else { 536 | res.coefs[sONE]= this.F.add(res.coefs[sONE], v); 537 | } 538 | delete res.coefs[k]; 539 | } else if (s != k) { 540 | if (!utils.isDefined(res.coefs[s])) { 541 | res.coefs[s]=res.coefs[k]; 542 | } else { 543 | res.coefs[s]= this.F.add(res.coefs[s], res.coefs[k]); 544 | } 545 | delete res.coefs[k]; 546 | } 547 | } 548 | for (let k in res.coefs) { 549 | if (this.F.isZero(res.coefs[k])) delete res.coefs[k]; 550 | } 551 | return res; 552 | } else if (a.t == "QEX") { 553 | const res = { 554 | t: "QEX", 555 | a: this.canonize(ctx, a.a), 556 | b: this.canonize(ctx, a.b), 557 | c: this.canonize(ctx, a.c) 558 | }; 559 | let keysa = Object.keys(res.a.coefs); 560 | if ((keysa.length==1) && (keysa[0]==0)) { 561 | res.b = this.mul(res.b, { 562 | t: "N", 563 | v: res.a.coefs[0] 564 | }); 565 | res.c = this.add(res.b, res.c); 566 | res.c = this.canonize(ctx, res.c); 567 | res.a = {t: "LC", coefs: {}}; 568 | res.b = {t: "LC", coefs: {}}; 569 | } 570 | let keysb = Object.keys(res.b.coefs); 571 | if ((keysb.length==1) && (keysb[0]==0)) { 572 | res.a = this.mul(res.a, { 573 | t: "N", 574 | v: res.b.coefs[0] 575 | }); 576 | res.c = this.add(res.a, res.c); 577 | res.c = this.canonize(ctx, res.c); 578 | res.a = {t: "LC", coefs: {}}; 579 | res.b = {t: "LC", coefs: {}}; 580 | } 581 | return res; 582 | } else { 583 | return a; 584 | } 585 | } 586 | } 587 | 588 | module.exports = LCAlgebra; 589 | 590 | 591 | 592 | 593 | 594 | 595 | -------------------------------------------------------------------------------- /src/r1csfile.js: -------------------------------------------------------------------------------- 1 | 2 | const fastFile = require("fastfile"); 3 | const assert = require("assert"); 4 | const BigArray = require("./bigarray"); 5 | 6 | module.exports.buildR1cs = buildR1cs; 7 | 8 | 9 | async function buildR1cs(ctx, fileName) { 10 | 11 | const fd = await fastFile.createOverride(fileName, 1<<24, 1<<22); 12 | 13 | const buffBigInt = new Uint8Array(ctx.F.n8); 14 | 15 | const type = "r1cs"; 16 | const buff = new Uint8Array(4); 17 | for (let i=0; i<4; i++) buff[i] = type.charCodeAt(i); 18 | await fd.write(buff, 0); // Magic "r1cs" 19 | 20 | await fd.writeULE32(1); // Version 21 | await fd.writeULE32(3); // Number of Sections 22 | 23 | // Write the header 24 | /////////// 25 | await fd.writeULE32(1); // Header type 26 | const pHeaderSize = fd.pos; 27 | await fd.writeULE64(0); // Temporally set to 0 length 28 | 29 | 30 | const n8 = (Math.floor( (ctx.F.bitLength - 1) / 64) +1)*8; 31 | // Field Def 32 | await fd.writeULE32(n8); // Temporally set to 0 length 33 | await writeBigInt(ctx.F.p); 34 | 35 | const NWires = 36 | ctx.totals[ctx.stONE] + 37 | ctx.totals[ctx.stOUTPUT] + 38 | ctx.totals[ctx.stPUBINPUT] + 39 | ctx.totals[ctx.stPRVINPUT] + 40 | ctx.totals[ctx.stINTERNAL]; 41 | 42 | await fd.writeULE32(NWires); 43 | await fd.writeULE32(ctx.totals[ctx.stOUTPUT]); 44 | await fd.writeULE32(ctx.totals[ctx.stPUBINPUT]); 45 | await fd.writeULE32(ctx.totals[ctx.stPRVINPUT]); 46 | await fd.writeULE64(ctx.signals.length); 47 | await fd.writeULE32(ctx.constraints.length); 48 | 49 | const headerSize = fd.pos - pHeaderSize - 8; 50 | 51 | // Write constraints 52 | /////////// 53 | await fd.writeULE32(2); // Constraints type 54 | const pConstraintsSize = fd.pos; 55 | await fd.writeULE64(0); // Temporally set to 0 length 56 | 57 | for (let i=0; i=0) continue; // If has an alias, continue.. 77 | assert(typeof outIdx != "undefined", `Signal ${i} does not have index`); 78 | if (outIdx>=NWires) continue; // Is a constant or a discarded variable 79 | if (typeof arr[ctx.signals[i].id] == "undefined") { 80 | arr[outIdx] = i; 81 | } 82 | } 83 | for (let i=0; i=0 ) lSignal = ctx.signals[lSignal.e]; 111 | if (lSignal.id >= NWires) throw new Error("Signal Coef A: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id); 112 | buffV.setUint32(o, lSignal.id, true); o+=4; 113 | ctx.F.toRprLE(buff, o, c.a.coefs[coef]); o+=n8; 114 | } 115 | 116 | buffV.setUint32(o, idxB.length, true); o+=4; 117 | for (let i=0; i=0 ) lSignal = ctx.signals[lSignal.e]; 121 | if (lSignal.id >= NWires) throw new Error("Signal Coef B: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id); 122 | buffV.setUint32(o, lSignal.id, true); o+=4; 123 | ctx.F.toRprLE(buff, o, c.b.coefs[coef]); o+=n8; 124 | } 125 | 126 | buffV.setUint32(o, idxC.length, true); o+=4; 127 | for (let i=0; i=0 ) lSignal = ctx.signals[lSignal.e]; 131 | if (lSignal.id >= NWires) throw new Error("Signal Coef C: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id); 132 | buffV.setUint32(o, lSignal.id, true); o+=4; 133 | ctx.F.toRprLE(buff, o, ctx.F.neg(c.c.coefs[coef])); o+=n8; 134 | } 135 | 136 | return fd.write(buff); 137 | } 138 | 139 | async function writeBigInt(n, pos) { 140 | 141 | ctx.F.toRprLE(buffBigInt, 0, n); 142 | 143 | await fd.write(buffBigInt, pos); 144 | 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/streamfromarray_bin.js: -------------------------------------------------------------------------------- 1 | 2 | const Readable = require("stream").Readable; 3 | 4 | module.exports = function streamFromArrayBin(a) { 5 | const rs = Readable(); 6 | 7 | let curIndex = 0; 8 | 9 | rs._read = function(size) { 10 | if (curIndex >= a.length) { 11 | rs.push(null); 12 | return; 13 | } 14 | const start = curIndex; 15 | const end = Math.min(a.length, curIndex+size); 16 | curIndex = end; 17 | rs.push(a.slice(start, end)); 18 | }; 19 | 20 | return rs; 21 | }; 22 | -------------------------------------------------------------------------------- /src/streamfromarray_txt.js: -------------------------------------------------------------------------------- 1 | const Readable = require("stream").Readable; 2 | 3 | module.exports = function streamFromArrayTxt(ma) { 4 | const rs = Readable(); 5 | 6 | let curIndex = getFirstIdx(ma); 7 | 8 | rs._read = function() { 9 | let res; 10 | res = objFromIdx(ma, curIndex); 11 | curIndex = nextIdx(curIndex); 12 | if (res!==null) { 13 | rs.push(res + "\n"); 14 | } else { 15 | rs.push(null); 16 | } 17 | }; 18 | 19 | 20 | return rs; 21 | 22 | 23 | function getFirstIdx(ma) { 24 | if (typeof ma.push !== "function" ) return []; 25 | return [0, ...getFirstIdx(ma[0])]; 26 | } 27 | 28 | function nextIdx(idx) { 29 | if (idx == null) return null; 30 | if (idx.length == 0) return null; 31 | 32 | const parentIdx = idx.slice(0,-1); 33 | 34 | const itObj = objFromIdx(ma, parentIdx); 35 | const newLastIdx = idx[idx.length-1]+1; 36 | if (newLastIdx < itObj.length) { 37 | const resIdx = idx.slice(); 38 | resIdx[resIdx.length-1] = newLastIdx; 39 | return [...resIdx, ...getFirstIdx(itObj[newLastIdx])]; 40 | } else { 41 | return nextIdx(parentIdx); 42 | } 43 | } 44 | 45 | function objFromIdx(ma, idx) { 46 | if (idx == null) return null; 47 | if (idx.length == 0) return ma; 48 | if (ma.length == 0) return ""; 49 | return objFromIdx(ma[idx[0]], idx.slice(1)); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const fnv = require("fnv-plus"); 2 | 3 | module.exports.ident =ident; 4 | 5 | module.exports.extractSizes =extractSizes; 6 | module.exports.flatArray = flatArray; 7 | module.exports.csArr = csArr; 8 | module.exports.accSizes = accSizes; 9 | module.exports.fnvHash = fnvHash; 10 | module.exports.sameSizes = sameSizes; 11 | module.exports.isDefined = isDefined; 12 | module.exports.accSizes2Str = accSizes2Str; 13 | module.exports.setUint64 = setUint64; 14 | 15 | function ident(text) { 16 | if (typeof text === "string") { 17 | let lines = text.split("\n"); 18 | for (let i=0; i=0; i--) { 66 | accSizes.unshift(accSizes[0]*sizes[i]); 67 | } 68 | return accSizes; 69 | } 70 | 71 | function fnvHash(str) { 72 | return fnv.hash(str, 64).hex(); 73 | } 74 | 75 | function sameSizes(s1, s2) { 76 | if (!Array.isArray(s1)) return false; 77 | if (!Array.isArray(s2)) return false; 78 | if (s1.length != s2.length) return false; 79 | for (let i=0; i>> 0; 96 | const sMSB = (n - sLSB) / 0x100000000; 97 | buffV.setUint32(o, sLSB , true); 98 | buffV.setUint32(o+4, sMSB , true); 99 | } 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /test/basiccases.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const Scalar = require("ffjavascript").Scalar; 4 | const F1Field = require("ffjavascript").F1Field; 5 | const c_tester = require("../index.js").c_tester; 6 | const wasm_tester = require("../index.js").wasm_tester; 7 | 8 | const __P__ = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 9 | 10 | const Fr = new F1Field(__P__); 11 | 12 | const basicCases = require("./basiccases.json"); 13 | 14 | function normalize(o) { 15 | if ((typeof(o) == "bigint") || o.isZero !== undefined) { 16 | return Fr.e(o); 17 | } else if (Array.isArray(o)) { 18 | return o.map(normalize); 19 | } else if (typeof o == "object") { 20 | const res = {}; 21 | for (let k in o) { 22 | res[k] = normalize(o[k]); 23 | } 24 | return res; 25 | } else { 26 | return Fr.e(o); 27 | } 28 | 29 | } 30 | 31 | 32 | async function doTest(tester, circuit, testVectors) { 33 | const cir = await tester(path.join(__dirname, "circuits", circuit)); 34 | 35 | for (let i=0; i { 51 | await doTest(c_tester, basicCases[i].circuit, basicCases[i].tv); 52 | }); 53 | } 54 | 55 | for (let i=0; i { 57 | await doTest(wasm_tester, basicCases[i].circuit, basicCases[i].tv); 58 | }); 59 | } 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /test/basiccases.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "inout", 4 | "circuit": "inout.circom", 5 | "tv": [ 6 | [{ 7 | "in1": 1, 8 | "in2": [2,3], 9 | "in3" : [[4,5], [6,7], [8,9]] 10 | }, { 11 | "out1": 1, 12 | "out2": [2,3], 13 | "out3": [[4,5], [6,7],[8,9]] 14 | }] 15 | ] 16 | }, 17 | { 18 | "name": "fnarray", 19 | "circuit": "fnarray.circom", 20 | "tv": [ 21 | [{ 22 | "in": [1, 8, 25] 23 | }, { 24 | "out": [2, 16, 50] 25 | }] 26 | ] 27 | }, 28 | { 29 | "name": "add", 30 | "circuit": "add.circom", 31 | "tv": [ 32 | [{"in": [0,0]}, {"out": 0}], 33 | [{"in": [0 ,1]}, {"out": 1}], 34 | [{"in": [1 ,2]}, {"out": 3}], 35 | [{"in": [-1,1]}, {"out": 0}] 36 | ] 37 | }, 38 | { 39 | "name": "add constant", 40 | "circuit": "addconst1.circom", 41 | "tv": [ 42 | [{"in": 0}, {"out": 15}], 43 | [{"in": 10}, {"out": 25}], 44 | [{"in": -2}, {"out": 13}] 45 | ] 46 | }, 47 | { 48 | "name": "for unrolled", 49 | "circuit": "forunrolled.circom", 50 | "tv": [ 51 | [{"in": 0}, {"out": [ 0, 1, 2]}], 52 | [{"in": 10}, {"out": [10, 11, 12]}], 53 | [{"in": -2}, {"out": [-2, -1, 0]}] 54 | ] 55 | }, 56 | { 57 | "name": "for rolled", 58 | "circuit": "forrolled.circom", 59 | "tv": [ 60 | [{"in": 0}, {"out": 0}], 61 | [{"in": 10}, {"out": 10}] 62 | ] 63 | }, 64 | { 65 | "name": "while unrolled", 66 | "circuit": "whileunrolled.circom", 67 | "tv": [ 68 | [{"in": 0}, {"out": [ 0, 1, 2]}], 69 | [{"in": 10}, {"out": [10, 11, 12]}], 70 | [{"in": -2}, {"out": [-2, -1, 0]}] 71 | ] 72 | }, 73 | { 74 | "name": "while rolled", 75 | "circuit": "whilerolled.circom", 76 | "tv": [ 77 | [{"in": 0}, {"out": 0}], 78 | [{"in": 10}, {"out": 10}] 79 | ] 80 | }, 81 | { 82 | "name": "function1", 83 | "circuit": "function1.circom", 84 | "tv": [ 85 | [{"in": 0}, {"out": 3}], 86 | [{"in": 10}, {"out": 13}], 87 | [{"in": -2}, {"out": 1}] 88 | ] 89 | }, 90 | { 91 | "name": "function2", 92 | "circuit": "function2.circom", 93 | "tv": [ 94 | [{"in": 0 }, {"out": 3}], 95 | [{"in": 10}, {"out": 13}], 96 | [{"in": -2}, {"out": 1}] 97 | ] 98 | }, 99 | { 100 | "name": "constants1", 101 | "circuit": "constants1.circom", 102 | "tv": [ 103 | [{"in": 0}, {"out": 42}], 104 | [{"in": 10}, {"out": 52}], 105 | [{"in": -2}, {"out": 40}] 106 | ] 107 | }, 108 | { 109 | "name": "arrays", 110 | "circuit": "arrays.circom", 111 | "tv": [ 112 | [{"in": 0}, {"out": [ 1, 8, 51]}], 113 | [{"in": 10}, {"out": [11, 28, 111]}], 114 | [{"in": -2}, {"out": [-1, 4, 39]}] 115 | ] 116 | }, 117 | { 118 | "name": "if unrolled", 119 | "circuit": "ifunrolled.circom", 120 | "tv": [ 121 | [{"in": 0}, {"out": [ 1, 3, 6]}], 122 | [{"in": 10}, {"out": [11, 13, 16]}], 123 | [{"in": -2}, {"out": [-1, 1, 4]}] 124 | ] 125 | }, 126 | { 127 | "name": "if rolled", 128 | "circuit": "ifrolled.circom", 129 | "tv": [ 130 | [{"in": 0}, {"out": [1, 0, 0]}], 131 | [{"in": 1}, {"out": [0, 1, 0]}], 132 | [{"in": 2}, {"out": [0, 0, 1]}], 133 | [{"in": 3}, {"out": [0, 0, 0]}], 134 | [{"in": -2}, {"out": [0, 0, 0]}] 135 | ] 136 | }, 137 | { 138 | "name": "inc", 139 | "circuit": "inc.circom", 140 | "tv": [ 141 | [{"in": 0}, {"out": [5, 2]}], 142 | [{"in": 1}, {"out": [6, 4]}], 143 | [{"in": 2}, {"out": [7, 6]}], 144 | [{"in": 3}, {"out": [8, 8]}], 145 | [{"in": -2}, {"out": [3,-2]}] 146 | ] 147 | }, 148 | { 149 | "name": "dec", 150 | "circuit": "dec.circom", 151 | "tv": [ 152 | [{"in": 0}, {"out": [ 1, -2]}], 153 | [{"in": 1}, {"out": [ 2, 0]}], 154 | [{"in": 2}, {"out": [ 3, 2]}], 155 | [{"in": 3}, {"out": [ 4, 4]}], 156 | [{"in": -2}, {"out": [-1, -6]}] 157 | ] 158 | }, 159 | { 160 | "name": "ops", 161 | "circuit": "ops.circom", 162 | "tv": [ 163 | [{"in": [-2, 2]}, {"add": 0, "sub": -4, "mul": -4}], 164 | [{"in": [-1, 1]}, {"add": 0, "sub": -2, "mul": -1}], 165 | [{"in": [ 0, 0]}, {"add": 0, "sub": 0, "mul": 0}], 166 | [{"in": [ 1,-1]}, {"add": 0, "sub": 2, "mul": -1}], 167 | [{"in": [ 2,-2]}, {"add": 0, "sub": 4, "mul": -4}], 168 | [{"in": [-2,-3]}, {"add": -5, "sub": 1, "mul": 6}], 169 | [{"in": [ 2, 3]}, {"add": 5, "sub": -1, "mul": 6}] 170 | ] 171 | }, 172 | { 173 | "name": "ops2", 174 | "circuit": "ops2.circom", 175 | "tv": [ 176 | [{"in": [-2, 2]}, {"div": -1, "idiv": "10944121435919637611123202872628637544274182200208017171849102093287904247807", "mod": 1}], 177 | [{"in": [-1, 1]}, {"div": -1, "idiv": -1, "mod": 0}], 178 | [{"in": [ 1,-1]}, {"div": -1, "idiv": 0, "mod": 1}] 179 | ] 180 | }, 181 | { 182 | "name": "ops3", 183 | "circuit": "ops3.circom", 184 | "tv": [ 185 | [{"in": [-2, 2]}, {"neg1": 2, "neg2": -2, "pow": 4}], 186 | [{"in": [ 0, 1]}, {"neg1": 0, "neg2": -1, "pow": 0}], 187 | [{"in": [ 1,-1]}, {"neg1": -1, "neg2": 1, "pow": 1}] 188 | ] 189 | }, 190 | { 191 | "name": "Comparation ops", 192 | "circuit": "opscmp.circom", 193 | "tv": [ 194 | [{"in": [ 8, 9]}, {"lt": 1, "leq": 1, "eq":0, "neq":1, "geq": 0, "gt":0}], 195 | [{"in": [-2,-2]}, {"lt": 0, "leq": 1, "eq":1, "neq":0, "geq": 1, "gt":0}], 196 | [{"in": [-1,-2]}, {"lt": 0, "leq": 0, "eq":0, "neq":1, "geq": 1, "gt":1}], 197 | [{"in": [ 1,-1]}, {"lt": 0, "leq": 0, "eq":0, "neq":1, "geq": 1, "gt":1}] 198 | ] 199 | }, 200 | { 201 | "name": "Bit ops", 202 | "circuit": "opsbit.circom", 203 | "tv": [ 204 | [ 205 | { 206 | "in": [ 5, 3] 207 | }, 208 | { 209 | "and": 1, 210 | "or": 7, 211 | "xor":6, 212 | "not1": "7059779437489773633646340506914701874769131765994106666166191815402473914361", 213 | "shl": 40, 214 | "shr":0 215 | } 216 | ], 217 | [ 218 | { 219 | "in": [ 0, 0] 220 | }, 221 | { 222 | "and": 0, 223 | "or": 0, 224 | "xor":0, 225 | "not1":"7059779437489773633646340506914701874769131765994106666166191815402473914366", 226 | "shl": 0, 227 | "shr":0 228 | } 229 | ], 230 | [ 231 | { 232 | "in": [-1, 1] 233 | }, 234 | { 235 | "and": 0, 236 | "or": 0, 237 | "xor": 0, 238 | "not1": "7059779437489773633646340506914701874769131765994106666166191815402473914367", 239 | "shl": "14828463434349501588600065238342573213779232634421927677532012371173334581248", 240 | "shr": "10944121435919637611123202872628637544274182200208017171849102093287904247808" 241 | } 242 | ] 243 | ] 244 | }, 245 | { 246 | "name": "Logical ops", 247 | "circuit": "opslog.circom", 248 | "tv": [ 249 | [{"in": [ 5, 0]}, {"and": 0, "or": 1, "not1":0}], 250 | [{"in": [ 0, 1]}, {"and": 0, "or": 1, "not1":1}], 251 | [{"in": [-1, 9]}, {"and": 1, "or": 1, "not1":0}], 252 | [{"in": [ 0, 0]}, {"and": 0, "or": 0, "not1":1}] 253 | ] 254 | }, 255 | { 256 | "name": "Conditional Ternary operator", 257 | "circuit": "condternary.circom", 258 | "tv": [ 259 | [{"in": 0}, {"out": 21}], 260 | [{"in": 1}, {"out": 1}], 261 | [{"in": 2}, {"out": 23}], 262 | [{"in":-1}, {"out": 20}] 263 | ] 264 | }, 265 | { 266 | "name": "Compute block", 267 | "circuit": "compute.circom", 268 | "tv": [ 269 | [{"x": 1}, {"y": 7}], 270 | [{"x": 2}, {"y": 7}], 271 | [{"x": 3}, {"y": 11}], 272 | [{"x":-1}, {"y": -5}] 273 | ] 274 | }, 275 | { 276 | "name": "Component array", 277 | "circuit": "componentarray.circom", 278 | "tv": [ 279 | [{"in": 1}, {"out": 1}], 280 | [{"in": 2}, {"out": 256}], 281 | [{"in": 3}, {"out": 6561}], 282 | [{"in":-1}, {"out": 1}] 283 | ] 284 | }, 285 | { 286 | "name": "Component array 2d", 287 | "circuit": "componentarray2.circom", 288 | "tv": [ 289 | [{"in": [1,2]}, {"out": [1, 256]}], 290 | [{"in": [0,3]}, {"out": [0, 6561]}] 291 | ] 292 | }, 293 | { 294 | "name": "Constant circuit", 295 | "circuit": "constantcircuit.circom", 296 | "tv": [ 297 | [{}, {"out": [1,0,1,0, 0,0,0,1, 0,1,1,1, 0,1,0,1, 1,1,1,0, 0,1,1,0, 1,1,0,1, 1,1,0,1]}] 298 | ] 299 | }, 300 | { 301 | "name": "Constant internal circuit", 302 | "circuit": "constantinternalcircuit.circom", 303 | "tv": [ 304 | [{"in": 1}, {"out": 5}], 305 | [{"in": 0}, {"out": 4}], 306 | [{"in": -2}, {"out": 2}], 307 | [{"in": 10}, {"out": 14}] 308 | ] 309 | }, 310 | { 311 | "name": "include", 312 | "circuit": "include.circom", 313 | "tv": [ 314 | [{"in": 3}, {"out": 6}], 315 | [{"in": 6}, {"out": 15}] 316 | ] 317 | }, 318 | { 319 | "name": "poma", 320 | "circuit": "poma.circom", 321 | "tv": [ 322 | [{}, {}] 323 | ] 324 | }, 325 | { 326 | "name": "modify_left", 327 | "circuit": "modify_left.circom", 328 | "tv": [ 329 | [{}, {"out": [3,4]}] 330 | ] 331 | }, 332 | { 333 | "name": "poma2", 334 | "circuit": "poma2.circom", 335 | "tv": [ 336 | [{}, {}] 337 | ] 338 | } 339 | ] 340 | -------------------------------------------------------------------------------- /test/circuits/add.circom: -------------------------------------------------------------------------------- 1 | 2 | template Add() { 3 | signal input in[2]; 4 | signal output out; 5 | 6 | out <== in[0] + in[1]; 7 | } 8 | 9 | component main = Add(); 10 | -------------------------------------------------------------------------------- /test/circuits/addconst1.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | template AddConst(c) { 4 | signal input in; 5 | signal output out; 6 | var a = 2; 7 | var b = 3; 8 | a=a+b; 9 | a=a+4; 10 | a=a+c; 11 | 12 | out <== 5 + a + in; 13 | } 14 | 15 | // It should out <== in + 1+2+3+4+5 = in + 15 16 | component main = AddConst(1); 17 | -------------------------------------------------------------------------------- /test/circuits/arrays.circom: -------------------------------------------------------------------------------- 1 | // arr1 2 | 3 | 4 | function Add3(arr1, arr2, arr3) { 5 | var res[3]; 6 | 7 | res[0] = arr1; 8 | res[1] = 0; 9 | for (var i=0; i<2; i += 1) { 10 | res[1] = res[1] + arr2[i]; 11 | } 12 | 13 | res[2] = 0; 14 | for (var i=0; i<2; i++) { 15 | for (var j=0; j<3; j += 1) { 16 | res[2] = res[2] + arr3[i][j]; 17 | } 18 | } 19 | 20 | return res; 21 | } 22 | 23 | template Main() { 24 | signal input in; 25 | signal output out[3]; 26 | 27 | var c[3] = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39]; 28 | var d[3] = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]); 29 | 30 | out[0] <-- d[0] + c[0]; 31 | out[0] === in+c[0]; 32 | 33 | out[1] <-- d[1]+c[1]; 34 | // out[1] === (in+in)+3+c[1]; 35 | out[1] === 2*in+3+c[1]; 36 | 37 | out[2] <-- d[2]+c[2]; 38 | // out[2] === (in+in+in+in+in+in)+12+c[2]; 39 | out[2] === 6*in+12+c[2]; 40 | } 41 | 42 | component main = Main(); 43 | -------------------------------------------------------------------------------- /test/circuits/assignsignal.circom: -------------------------------------------------------------------------------- 1 | template A() { 2 | signal output out; 3 | 4 | out = 3; // This is an error that compile should detect 5 | } 6 | 7 | component main = A(); 8 | -------------------------------------------------------------------------------- /test/circuits/componentarray.circom: -------------------------------------------------------------------------------- 1 | template Square() { 2 | signal input in; 3 | signal output out; 4 | 5 | out <== in*in; 6 | } 7 | 8 | template Main(n) { 9 | signal input in; 10 | signal output out; 11 | 12 | component squares[n]; 13 | 14 | var i; 15 | 16 | for (i=0; i out; 26 | } 27 | 28 | component main = Main(3); 29 | -------------------------------------------------------------------------------- /test/circuits/componentarray2.circom: -------------------------------------------------------------------------------- 1 | template Square() { 2 | signal input in; 3 | signal output out; 4 | 5 | out <== in**2; 6 | } 7 | 8 | template Main(n, nrounds) { 9 | signal input in[n]; 10 | signal output out[n]; 11 | 12 | component squares[n][nrounds]; 13 | 14 | for (var i=0; i out[i]; 24 | } 25 | } 26 | 27 | component main = Main(2, 3); 28 | -------------------------------------------------------------------------------- /test/circuits/compute.circom: -------------------------------------------------------------------------------- 1 | template X() { 2 | signal input x; 3 | signal output y; 4 | signal x2; 5 | signal x3; 6 | var a; 7 | compute { 8 | a = (x*x*x+6)/x; 9 | y <-- a; 10 | } 11 | 12 | x2 <== x*x; 13 | x3 <== x2*x; 14 | x*y === x3+6; 15 | } 16 | 17 | component main = X(); 18 | -------------------------------------------------------------------------------- /test/circuits/condternary.circom: -------------------------------------------------------------------------------- 1 | template CondTernary() { 2 | signal input in; 3 | signal output out; 4 | 5 | var a = 3; 6 | var b = a==3 ? 1 : 2; // b is 1 7 | var c = a!=3 ? 10 : 20; // c is 20 8 | var d = b+c; // d is 21 9 | 10 | 11 | out <-- ((in & 1) != 1) ? in + d : in; // Add 21 if in is pair 12 | 13 | } 14 | 15 | component main = CondTernary() 16 | -------------------------------------------------------------------------------- /test/circuits/constantcircuit.circom: -------------------------------------------------------------------------------- 1 | template H(x) { 2 | signal output out[32]; 3 | var c[8] = [0x6a09e667, 4 | 0xbb67ae85, 5 | 0x3c6ef372, 6 | 0xa54ff53a, 7 | 0x510e527f, 8 | 0x9b05688c, 9 | 0x1f83d9ab, 10 | 0x5be0cd19]; 11 | 12 | for (var i=0; i<32; i++) { 13 | out[i] <== (c[x] >> i) & 1; 14 | } 15 | } 16 | 17 | component main = H(1); 18 | -------------------------------------------------------------------------------- /test/circuits/constantinternalcircuit.circom: -------------------------------------------------------------------------------- 1 | 2 | template Const() { 3 | signal output out[2]; 4 | 5 | out[0] <== 2; 6 | out[1] <== 2; 7 | } 8 | 9 | template Main() { 10 | signal input in; 11 | signal output out; 12 | 13 | component const = Const(); 14 | 15 | out <== const.out[0] + const.out[1] + in; 16 | } 17 | 18 | component main = Main(); 19 | -------------------------------------------------------------------------------- /test/circuits/constants1.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | template Add(n) { 5 | signal input in[n]; 6 | signal output out; 7 | 8 | var lc = 0; 9 | for (var i=0; i 3 9 | var e = --c; // e --> 1 10 | 11 | out[0] <== in + e; // in + 1 12 | 13 | // Then play with signals 14 | 15 | c = in; 16 | d = c--; //d <-- in; 17 | e = --c; // d <-- in-2 18 | 19 | out[1] <== in + e; // 2*in -2 20 | 21 | } 22 | 23 | component main = Main(); 24 | -------------------------------------------------------------------------------- /test/circuits/declareandistantiate.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | template A() { 5 | signal a; 6 | } 7 | 8 | template B() { 9 | component a[2] = A(); 10 | } 11 | 12 | component main = B(); 13 | -------------------------------------------------------------------------------- /test/circuits/fnarray.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | function calc(h) { 6 | var res[3]; 7 | for (var i=0; i<3; i++) res[i] = h[i]*2; 8 | return res; 9 | } 10 | 11 | template Test() { 12 | signal input in[3]; 13 | signal output out[3]; 14 | 15 | var cout[3] = calc(in); 16 | for (var i=0; i<3; i++) out[i] <-- cout[i]; 17 | 18 | for (var i=0; i<3; i++) out[i] === in[i]*2; 19 | } 20 | 21 | component main = Test(); 22 | -------------------------------------------------------------------------------- /test/circuits/forrolled.circom: -------------------------------------------------------------------------------- 1 | template ForRolled() { 2 | signal input in; 3 | signal output out; 4 | 5 | var acc = 0; 6 | 7 | for (var i=0; i 3 9 | var e = ++c; // e --> 5 10 | 11 | out[0] <== in + e; // in + 5 12 | 13 | // Then play with signals 14 | 15 | c = in; 16 | d = c++; //d <-- in; 17 | e = ++c; // d <-- in+2 18 | 19 | out[1] <== in + e; // 2*in +2 20 | 21 | } 22 | 23 | component main = Main(); 24 | 25 | -------------------------------------------------------------------------------- /test/circuits/include.circom: -------------------------------------------------------------------------------- 1 | include "included.circom"; 2 | include "included.circom"; // Include twice is fine. The second one is not included 3 | 4 | template Main() { 5 | signal input in; 6 | signal output out; 7 | 8 | component t1 = T1(); 9 | 10 | var a = F1(3); 11 | 12 | in ==> t1.in; 13 | t1.out + a ==> out; /// out <-- in**2/3+3 14 | } 15 | 16 | component main = Main(); 17 | -------------------------------------------------------------------------------- /test/circuits/included.circom: -------------------------------------------------------------------------------- 1 | template T1() { 2 | signal input in; 3 | signal output out; 4 | 5 | out <== in**2/3; 6 | } 7 | 8 | function F1(a) { 9 | return a**2/3; 10 | } 11 | -------------------------------------------------------------------------------- /test/circuits/inout.circom: -------------------------------------------------------------------------------- 1 | template Internal() { 2 | signal input in1; 3 | signal input in2[2]; 4 | signal input in3[3][2]; 5 | 6 | signal output out1; 7 | signal output out2[2]; 8 | signal output out3[3][2]; 9 | 10 | out1 <== in1; 11 | out2[0] <== in2[0]; 12 | out2[1] <== in2[1]; 13 | 14 | out3[0][0] <== in3[0][0]; 15 | out3[0][1] <== in3[0][1]; 16 | out3[1][0] <== in3[1][0]; 17 | out3[1][1] <== in3[1][1]; 18 | out3[2][0] <== in3[2][0]; 19 | out3[2][1] <== in3[2][1]; 20 | } 21 | 22 | template InOut() { 23 | signal input in1; 24 | signal input in2[2]; 25 | signal input in3[3][2]; 26 | 27 | signal output out1; 28 | signal output out2[2]; 29 | signal output out3[3][2]; 30 | 31 | component internal = Internal(); 32 | 33 | internal.in1 <== in1; 34 | internal.in2[0] <== in2[0]; 35 | internal.in2[1] <== in2[1]; 36 | internal.in3[0][0] <== in3[0][0]; 37 | internal.in3[0][1] <== in3[0][1]; 38 | internal.in3[1][0] <== in3[1][0]; 39 | internal.in3[1][1] <== in3[1][1]; 40 | internal.in3[2][0] <== in3[2][0]; 41 | internal.in3[2][1] <== in3[2][1]; 42 | 43 | internal.out1 ==> out1; 44 | internal.out2[0] ==> out2[0]; 45 | internal.out2[1] ==> out2[1]; 46 | internal.out3[0][0] ==> out3[0][0]; 47 | internal.out3[0][1] ==> out3[0][1]; 48 | internal.out3[1][0] ==> out3[1][0]; 49 | internal.out3[1][1] ==> out3[1][1]; 50 | internal.out3[2][0] ==> out3[2][0]; 51 | internal.out3[2][1] ==> out3[2][1]; 52 | } 53 | 54 | component main = InOut(); 55 | -------------------------------------------------------------------------------- /test/circuits/mixvarsignal.circom: -------------------------------------------------------------------------------- 1 | template X() { 2 | signal input i; 3 | signal output out; 4 | 5 | var r = 0; 6 | for (var n=0; n> in[1]; 16 | } 17 | 18 | component main = OpsBit(); 19 | -------------------------------------------------------------------------------- /test/circuits/opscmp.circom: -------------------------------------------------------------------------------- 1 | template OpsCmp() { 2 | signal input in[2]; 3 | signal output lt; 4 | signal output leq; 5 | signal output eq; 6 | signal output neq; 7 | signal output geq; 8 | signal output gt; 9 | 10 | lt <-- in[0] < in[1]; 11 | leq <-- in[0] <= in[1]; 12 | eq <-- in[0] == in[1]; 13 | neq <-- in[0] != in[1]; 14 | geq <-- in[0] >= in[1]; 15 | gt <-- in[0] > in[1]; 16 | } 17 | 18 | component main = OpsCmp(); 19 | -------------------------------------------------------------------------------- /test/circuits/opslog.circom: -------------------------------------------------------------------------------- 1 | template OpsLog() { 2 | signal input in[2]; 3 | signal output and; 4 | signal output or; 5 | signal output not1; 6 | 7 | and <-- in[0] && in[1]; 8 | or <-- in[0] || in[1]; 9 | not1 <-- !in[0]; 10 | } 11 | 12 | component main = OpsLog(); 13 | -------------------------------------------------------------------------------- /test/circuits/poma.circom: -------------------------------------------------------------------------------- 1 | template test() { 2 | signal out1[4]; 3 | signal out2[10]; 4 | 5 | for(var i = 0; i < 4; i++) { 6 | out1[i] <== i == 2 ? 1 : 2; 7 | } 8 | 9 | // this becomes an endless loop 10 | for(var i = 0; i < 10; i++) { 11 | log(i); 12 | // if you comment out this line, the `Signal assigned twice` error disappears and loop will indefinitely log `1` 13 | out2[i] <== i; 14 | } 15 | } 16 | 17 | component main = test() 18 | 19 | -------------------------------------------------------------------------------- /test/circuits/poma2.circom: -------------------------------------------------------------------------------- 1 | 2 | 3 | template sum() { 4 | signal input in[2]; 5 | signal output out; 6 | out <== in[0] + in[1]; 7 | } 8 | 9 | 10 | template Test() { 11 | component hasher = sum(); 12 | hasher.in[0] <== 1; 13 | hasher.in[1] <== 2; 14 | } 15 | 16 | component main = Test(); 17 | 18 | -------------------------------------------------------------------------------- /test/circuits/undefinedif.circom: -------------------------------------------------------------------------------- 1 | template X() { 2 | signal input i; 3 | signal input j; 4 | signal output out; 5 | 6 | if (i == 0) { 7 | out <-- i; 8 | } 9 | else { 10 | out <-- j; 11 | } 12 | } 13 | 14 | component main = X(); 15 | -------------------------------------------------------------------------------- /test/circuits/whilerolled.circom: -------------------------------------------------------------------------------- 1 | template WhileRolled() { 2 | signal input in; 3 | signal output out; 4 | 5 | var acc = 0; 6 | 7 | var i=0; 8 | while (i "); 42 | if (arr.length!=2) continue; 43 | outLines.push(symbols[arr[0]] + " --> " + arr[1]); 44 | } 45 | await fs.promises.writeFile(outFile,outLines.join("\n"), "utf8"); 46 | } 47 | 48 | 49 | run().then(() => { 50 | process.exit(0); 51 | }); 52 | --------------------------------------------------------------------------------