mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 11:16:42 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fc1bf5e97 |
@@ -1,4 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
"plugins": [
|
||||||
|
"mocha"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
@@ -24,6 +27,7 @@ module.exports = {
|
|||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always"
|
"always"
|
||||||
]
|
],
|
||||||
|
"mocha/no-exclusive-tests": "error"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -61,9 +61,3 @@ typings/
|
|||||||
.next
|
.next
|
||||||
|
|
||||||
tmp
|
tmp
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Workspace files are user-specific
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
|
|||||||
2
COPYING
2
COPYING
@@ -1,7 +1,7 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2020 0Kims Association <https://0kims.org>
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": ".",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"SublimeAnarchyDebug": {
|
|
||||||
"debug": {
|
|
||||||
"executable": "${project_path}/test/circuits/add",
|
|
||||||
"params": [
|
|
||||||
"addin.json",
|
|
||||||
"out.bin",
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
],
|
|
||||||
"environment": [
|
|
||||||
],
|
|
||||||
"working_dir": "${project_path}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
27
README.md
27
README.md
@@ -1,18 +1,12 @@
|
|||||||
# Circom
|
# Circon
|
||||||
|
|
||||||
Circom is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
Circon is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
||||||
|
|
||||||
In particular, it is designed to work in [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
In particular, it is designed to work in [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Tutorial
|
|
||||||
|
|
||||||
A good starting point [is this tutorial](https://github.com/iden3/circom/blob/master/TUTORIAL.md)
|
|
||||||
|
|
||||||
Also this [video](https://www.youtube.com/watch?v=-9TJa1hVsKA) is a good starting point.
|
|
||||||
|
|
||||||
### First circuit
|
### First circuit
|
||||||
|
|
||||||
Creation of a circuit. This is an example of a NAND door:
|
Creation of a circuit. This is an example of a NAND door:
|
||||||
@@ -31,7 +25,7 @@ template NAND() {
|
|||||||
component main = NAND();
|
component main = NAND();
|
||||||
```
|
```
|
||||||
|
|
||||||
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the following constraints:
|
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the constraints:
|
||||||
|
|
||||||
`<==` , `==>` : These two operators are used to connect signals and at the same time imply a constraint.
|
`<==` , `==>` : These two operators are used to connect signals and at the same time imply a constraint.
|
||||||
|
|
||||||
@@ -45,7 +39,7 @@ In the above example, both inputs are forced to be binary by adding the constrai
|
|||||||
|
|
||||||
### Compilation the circuit
|
### Compilation the circuit
|
||||||
|
|
||||||
First of all, the compiler must be installed by typing:
|
First of all, the compiler must be installed typing:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install -g circom
|
npm install -g circom
|
||||||
@@ -54,7 +48,7 @@ npm install -g circom
|
|||||||
The circuit is compiled with the following command:
|
The circuit is compiled with the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
circom mycircuit.circom -o mycircuit.json
|
circom -s mycircuit.circom -o mycircuit.json
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting output ( `mycircuit.json` ) can be used in the [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
The resulting output ( `mycircuit.json` ) can be used in the [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||||
@@ -97,7 +91,7 @@ in === out[0]*2**0 + out[1]*2**1 + out[2]*2**2 + ... + out[n-1]*2**(n-1)
|
|||||||
```
|
```
|
||||||
|
|
||||||
We do this by using a variable `lc1` and adding each signal multiplied by its coefficient.
|
We do this by using a variable `lc1` and adding each signal multiplied by its coefficient.
|
||||||
This variable does not hold a value at compilation time, but it holds a linear combination and it is used in the last constraint:
|
This variable does not hold a value in compilation time, but it holds a linear combination and it is used in the last constraint:
|
||||||
|
|
||||||
```
|
```
|
||||||
lc1 === in;
|
lc1 === in;
|
||||||
@@ -177,7 +171,7 @@ To waranty binary outputs:
|
|||||||
.
|
.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
out[n+e-1] * (out[n+e-1] - 1) === 0
|
out[n+e-1] * (out[n+e-1] - 1) == 0
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -260,14 +254,9 @@ component main = Adder();
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
### More examples.
|
|
||||||
|
|
||||||
You can find more examples in this library of basic circits [circomlib](https://github.com/iden3/circomlib)
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
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.
|
Circon is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
258
TUTORIAL.md
258
TUTORIAL.md
@@ -1,258 +0,0 @@
|
|||||||
# circom and snarkjs tutorial
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 1. Installing the tools
|
|
||||||
|
|
||||||
### 1.1 Pre-requisites
|
|
||||||
|
|
||||||
If you don't have it installed yet, you need to install `Node.js`.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### 1.2 Install **circom** and **snarkjs**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install -g circom
|
|
||||||
npm install -g snarkjs
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 2. Working with a circuit
|
|
||||||
|
|
||||||
Let's create a circuit that will allow you to prove that you are able to factor a number!
|
|
||||||
|
|
||||||
### 2.1 Create a circuit in a new directory
|
|
||||||
|
|
||||||
1. Create an empty directory called `factor` where you will put all the files that you will use in this tutorial.
|
|
||||||
```
|
|
||||||
mkdir factor
|
|
||||||
cd factor
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
|
||||||
2. Create a new file named `circuit.circom` with the following content:
|
|
||||||
|
|
||||||
```
|
|
||||||
template Multiplier() {
|
|
||||||
signal private input a;
|
|
||||||
signal private input b;
|
|
||||||
signal output c;
|
|
||||||
|
|
||||||
c <== a*b;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Multiplier();
|
|
||||||
```
|
|
||||||
|
|
||||||
This circuit has 2 private input signals named `a` and `b` and one output named `c`.
|
|
||||||
|
|
||||||
The only thing that the circuit does is forcing the signal `c` to be the value of `a*b`
|
|
||||||
|
|
||||||
After declaring the `Multiplier` template, we instantiate it with a component named`main`.
|
|
||||||
|
|
||||||
Note: When compiling a circuit, a component named `main` must always exist.
|
|
||||||
|
|
||||||
### 2.2 Compile the circuit
|
|
||||||
|
|
||||||
We are now ready to compile the circuit. Run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
circom circuit.circom --r1cs --wasm --sym
|
|
||||||
```
|
|
||||||
|
|
||||||
The `--r1cs` option will generate `circuit.r1cs` (the r1cs constraint system of the circuit in binary format).
|
|
||||||
|
|
||||||
The `--wasm` option will generate `circuit.wasm` (the wasm code to generate the witness).
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
||||||
## 3. Taking the compiled circuit to *snarkjs*
|
|
||||||
|
|
||||||
Now that the circuit is compiled, we will continue with `snarkjs`.
|
|
||||||
Please note that you can always access the help of `snarkjs` by typing:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs --help
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.1 View information and stats regarding a circuit
|
|
||||||
|
|
||||||
To show general statistics of this circuit, you can run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs info -r circuit.r1cs
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also print the constraints of the circuit by running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs printconstraints -r circuit.r1cs -s circuit.sym
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### 3.2 Setting up using *snarkjs*
|
|
||||||
|
|
||||||
|
|
||||||
Ok, let's run a setup for our circuit:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs setup
|
|
||||||
```
|
|
||||||
|
|
||||||
> By default `snarkjs` will look for and use `circuit.r1cs`. You can always specify a different circuit file by adding `-r <circuit R1CS file name>`.
|
|
||||||
|
|
||||||
The output of the setup will be in the form of 2 files: `proving_key.json` and `verification_key.json`.
|
|
||||||
|
|
||||||
### 3.3. Calculating a witness
|
|
||||||
|
|
||||||
Before creating any proof, we need to calculate all the signals of the circuit that match (all) the constraints of the circuit.
|
|
||||||
|
|
||||||
`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*.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
> Of course you can always use the number one and the same number as `a` or `b`. We will deal with this problem later.
|
|
||||||
|
|
||||||
So you want to prove that you know 3 and 11.
|
|
||||||
|
|
||||||
Let's create a file named `input.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"a": 3, "b": 11}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now let's calculate the witness:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs calculatewitness --wasm circuit.wasm --input input.json --witness witness.json
|
|
||||||
```
|
|
||||||
|
|
||||||
You may want to take a look at `witness.json` file with all the signals.
|
|
||||||
|
|
||||||
### Create the proof
|
|
||||||
|
|
||||||
Now that we have the witness generated, we can create the proof.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs proof
|
|
||||||
```
|
|
||||||
|
|
||||||
This command will use the `proving_key.json` and the `witness.json` files by default to generate `proof.json` and `public.json`
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
### Verifying the proof
|
|
||||||
|
|
||||||
To verify the proof run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs verify
|
|
||||||
```
|
|
||||||
|
|
||||||
This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid.
|
|
||||||
|
|
||||||
Here we are verifying that we know a witness that the public inputs and the outputs matches the ones in the `public.json` file.
|
|
||||||
|
|
||||||
|
|
||||||
If the proof is ok, you will see `OK` or `INVALID` if not ok.
|
|
||||||
|
|
||||||
### Generate the solidity verifier
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs generateverifier
|
|
||||||
```
|
|
||||||
|
|
||||||
This command will take the `verification_key.json` and generate solidity code in `verifier.sol` file.
|
|
||||||
|
|
||||||
You can take the code in `verifier.sol` and cut and paste it in remix.
|
|
||||||
|
|
||||||
This code contains two contracts: Pairings and Verifier. You only need to deploy the `Verifier` contract.
|
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
|
||||||
|
|
||||||
### Verifying the proof on-chain
|
|
||||||
|
|
||||||
The verifier contract deployed in the last step has a `view` function called `verifyProof`.
|
|
||||||
|
|
||||||
This function will return true if the proof and the inputs are valid.
|
|
||||||
|
|
||||||
To facilitate the call, you can use `snarkjs` to generate the parameters of the call by typing:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
snarkjs generatecall
|
|
||||||
```
|
|
||||||
|
|
||||||
Just cut and paste the output to the parameters field of the `verifyProof` method in Remix.
|
|
||||||
|
|
||||||
If every thing works ok, this method should return true.
|
|
||||||
|
|
||||||
If you change any bit in the parameters, the result will be verifiably false.
|
|
||||||
|
|
||||||
|
|
||||||
## Bonus track
|
|
||||||
|
|
||||||
We can fix the circuit to not accept the number 1 as any of the input values by adding some extra constraints.
|
|
||||||
|
|
||||||
Here, the trick is that we use the property that 0 has no inverse. So `(a-1)` should not have an inverse.
|
|
||||||
|
|
||||||
That means that `(a-1)*inv = 1` will be inpossible to match if `a` is 1.
|
|
||||||
|
|
||||||
We just calculate inv by `1/(a-1)`.
|
|
||||||
|
|
||||||
So, let's modify the circuit:
|
|
||||||
|
|
||||||
```
|
|
||||||
template Multiplier() {
|
|
||||||
signal private input a;
|
|
||||||
signal private input b;
|
|
||||||
signal output c;
|
|
||||||
signal inva;
|
|
||||||
signal invb;
|
|
||||||
|
|
||||||
inva <-- 1/(a-1);
|
|
||||||
(a-1)*inva === 1;
|
|
||||||
|
|
||||||
invb <-- 1/(b-1);
|
|
||||||
(b-1)*invb === 1;
|
|
||||||
|
|
||||||
c <== a*b;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Multiplier();
|
|
||||||
```
|
|
||||||
|
|
||||||
A nice thing of the circom language is that you can split a `<==` into two independent actions: `<--` and `===`.
|
|
||||||
|
|
||||||
The `<--` and `-->` operators assign a value to a signal without creating any constraints.
|
|
||||||
|
|
||||||
The `===` operator adds a constraint without assigning any value to a signal.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Where to go from here
|
|
||||||
|
|
||||||
You may want to read the [README](https://github.com/iden3/circom) to learn more features about `circom`.
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
||||||
Or a exponentiation in the Baby Jubjub curve [here](https://github.com/iden3/circomlib) (Work in progress).
|
|
||||||
|
|
||||||
|
|
||||||
# Final note
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Enjoy zero-knowledge proving!
|
|
||||||
49
circuits/gates.circom
Normal file
49
circuits/gates.circom
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
template XOR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a + b - 2*a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template AND() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template OR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a + b - a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NOT() {
|
||||||
|
signal input in;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== 1 + in - 2*in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NAND() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== 1 - a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NOR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a*b + 1 - a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
74
circuits/multiplexer.circom
Normal file
74
circuits/multiplexer.circom
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// --> Assignation without constraint
|
||||||
|
// <-- Assignation without constraint
|
||||||
|
// === Constraint
|
||||||
|
// <== Assignation with constraint
|
||||||
|
// ==> Assignation with constraint
|
||||||
|
// All variables are members of the field F[p]
|
||||||
|
// https://github.com/zcash-hackworks/sapling-crypto
|
||||||
|
// https://github.com/ebfull/bellman
|
||||||
|
|
||||||
|
/*
|
||||||
|
function log2(a) {
|
||||||
|
if (a==0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let n = 1;
|
||||||
|
let r = 1;
|
||||||
|
while (n<a) {
|
||||||
|
r++;
|
||||||
|
n *= 2;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
template EscalarProduct(w) {
|
||||||
|
signal input in1[w];
|
||||||
|
signal input in2[w];
|
||||||
|
signal output out;
|
||||||
|
signal aux[w];
|
||||||
|
var lc = 0;
|
||||||
|
for (var i=0; i<w; i++) {
|
||||||
|
aux[i] <== in1[i]*in2[i];
|
||||||
|
lc = lc + aux[i];
|
||||||
|
}
|
||||||
|
out <== lc;
|
||||||
|
}
|
||||||
|
|
||||||
|
template Decoder(w) {
|
||||||
|
signal input inp;
|
||||||
|
signal output out[w];
|
||||||
|
signal output success;
|
||||||
|
var lc=0;
|
||||||
|
|
||||||
|
for (var i=0; i<w; i++) {
|
||||||
|
out[i] <-- (inp == i) ? 1 : 0;
|
||||||
|
out[i] * (inp-i) === 0;
|
||||||
|
lc = lc + out[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
lc ==> success;
|
||||||
|
success * (success -1) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template Multiplexor(wIn, nIn) {
|
||||||
|
signal input inp[nIn][wIn];
|
||||||
|
signal input sel;
|
||||||
|
signal output out[wIn];
|
||||||
|
component Decoder(nIn) dec;
|
||||||
|
component EscalarProduct(nIn) ep[wIn];
|
||||||
|
sel ==> dec.inp;
|
||||||
|
for (var j=0; j<wIn; j++) {
|
||||||
|
for (var k=0; k<nIn; k++) {
|
||||||
|
inp[k][j] ==> ep[j].in1[k];
|
||||||
|
dec.out[k] ==> ep[j].in2[k];
|
||||||
|
}
|
||||||
|
ep[j].out ==> out[j];
|
||||||
|
}
|
||||||
|
dec.success === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
component Multiplexor(8,3) main;
|
||||||
|
|
||||||
|
|
||||||
74
circuits/sha256/binsum.circom
Normal file
74
circuits/sha256/binsum.circom
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Binary Sum
|
||||||
|
==========
|
||||||
|
|
||||||
|
This component creates a binary sum componet of ops operands and n bits each operand.
|
||||||
|
|
||||||
|
e is Number of carries: Depends on the number of operands in the input.
|
||||||
|
|
||||||
|
Main Constraint:
|
||||||
|
in[0][0] * 2^0 + in[0][1] * 2^1 + ..... + in[0][n-1] * 2^(n-1) +
|
||||||
|
+ in[1][0] * 2^0 + in[1][1] * 2^1 + ..... + in[1][n-1] * 2^(n-1) +
|
||||||
|
+ ..
|
||||||
|
+ in[ops-1][0] * 2^0 + in[ops-1][1] * 2^1 + ..... + in[ops-1][n-1] * 2^(n-1) +
|
||||||
|
===
|
||||||
|
out[0] * 2^0 + out[1] * 2^1 + + out[n+e-1] *2(n+e-1)
|
||||||
|
|
||||||
|
To waranty binary outputs:
|
||||||
|
|
||||||
|
out[0] * (out[0] - 1) === 0
|
||||||
|
out[1] * (out[0] - 1) === 0
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
out[n+e-1] * (out[n+e-1] - 1) == 0
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function calculates the number of extra bits in the output to do the full sum.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function nbits(a) {
|
||||||
|
var n = 1;
|
||||||
|
var r = 0;
|
||||||
|
while (n-1<a) {
|
||||||
|
r++;
|
||||||
|
n *= 2;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template BinSum(n, ops) {
|
||||||
|
var nout = nbits((2**n -1)*ops);
|
||||||
|
signal input in[ops][n];
|
||||||
|
signal output out[nout];
|
||||||
|
|
||||||
|
var lin = 0;
|
||||||
|
var lout = 0;
|
||||||
|
|
||||||
|
var k;
|
||||||
|
var j;
|
||||||
|
|
||||||
|
for (k=0; k<n; k++) {
|
||||||
|
for (j=0; j<ops; j++) {
|
||||||
|
lin += in[j][k] * 2**k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<nout; k++) {
|
||||||
|
out[k] <-- (lin >> k) & 1;
|
||||||
|
|
||||||
|
// Ensure out is binary
|
||||||
|
out[k] * (out[k] - 1) === 0;
|
||||||
|
|
||||||
|
lout += out[k] * 2**k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the sum;
|
||||||
|
|
||||||
|
lin === lout;
|
||||||
|
}
|
||||||
28
circuits/sha256/bitify.circom
Normal file
28
circuits/sha256/bitify.circom
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
template Num2Bits(n) {
|
||||||
|
signal input in;
|
||||||
|
signal output out[n];
|
||||||
|
var lc1=0;
|
||||||
|
|
||||||
|
for (var i = 0; i<n; i++) {
|
||||||
|
out[i] <-- (in >> i) & 1;
|
||||||
|
out[i] * (out[i] -1 ) === 0;
|
||||||
|
lc1 += out[i] * 2**i;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc1 === in;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template Bits2Num(n) {
|
||||||
|
signal input in[n];
|
||||||
|
signal output out;
|
||||||
|
var lc1=0;
|
||||||
|
|
||||||
|
for (var i = 0; i<n; i++) {
|
||||||
|
lc1 += in[i] * 2**i;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc1 ==> out;
|
||||||
|
}
|
||||||
27
circuits/sha256/ch.circom
Normal file
27
circuits/sha256/ch.circom
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* Ch
|
||||||
|
|
||||||
|
000 0
|
||||||
|
001 1
|
||||||
|
010 0
|
||||||
|
011 1
|
||||||
|
100 0
|
||||||
|
101 0
|
||||||
|
110 1
|
||||||
|
111 1
|
||||||
|
|
||||||
|
out = a&b ^ (!a)&c =>
|
||||||
|
|
||||||
|
out = a*(b-c) + c
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template Ch(n) {
|
||||||
|
signal input a[n];
|
||||||
|
signal input b[n];
|
||||||
|
signal input c[n];
|
||||||
|
signal output out[n];
|
||||||
|
|
||||||
|
for (var k=0; k<n; k++) {
|
||||||
|
out[k] <== a[k] * (b[k]-c[k]) + c[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
35
circuits/sha256/constants.circom
Normal file
35
circuits/sha256/constants.circom
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
|
||||||
|
template H(x) {
|
||||||
|
signal output out[32];
|
||||||
|
var c = [0x6a09e667,
|
||||||
|
0xbb67ae85,
|
||||||
|
0x3c6ef372,
|
||||||
|
0xa54ff53a,
|
||||||
|
0x510e527f,
|
||||||
|
0x9b05688c,
|
||||||
|
0x1f83d9ab,
|
||||||
|
0x5be0cd19];
|
||||||
|
|
||||||
|
for (var i=0; i<32; i++) {
|
||||||
|
out[i] <== (c[x] >> i) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template K(x) {
|
||||||
|
signal output out[32];
|
||||||
|
var c = [
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i=0; i<32; i++) {
|
||||||
|
out[i] <== (c[x] >> i) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
circuits/sha256/gates.circom
Normal file
49
circuits/sha256/gates.circom
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
template XOR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a + b - 2*a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template AND() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template OR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a + b - a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NOT() {
|
||||||
|
signal input in;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== 1 + in - 2*in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NAND() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== 1 - a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template NOR() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== a*b + 1 - a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
15
circuits/sha256/main.circom
Normal file
15
circuits/sha256/main.circom
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
include "sha256_2.jaz";
|
||||||
|
|
||||||
|
template Main() {
|
||||||
|
signal private input a;
|
||||||
|
signal private input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
component sha256_2 = SHA256_2();
|
||||||
|
|
||||||
|
sha256_2.a <== a;
|
||||||
|
sha256_2.b <== a;
|
||||||
|
out <== sha256_2.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = Main();
|
||||||
25
circuits/sha256/maj.circom
Normal file
25
circuits/sha256/maj.circom
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* Maj function for sha256
|
||||||
|
|
||||||
|
out = a&b ^ a&c ^ b&c =>
|
||||||
|
|
||||||
|
out = a*b + a*c + b*c - 2*a*b*c =>
|
||||||
|
|
||||||
|
out = a*( b + c - 2*b*c ) + b*c =>
|
||||||
|
|
||||||
|
mid = b*c
|
||||||
|
out = a*( b + c - 2*mid ) + mid
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template Maj(n) {
|
||||||
|
signal input a[n];
|
||||||
|
signal input b[n];
|
||||||
|
signal input c[n];
|
||||||
|
signal output out[n];
|
||||||
|
signal mid[n];
|
||||||
|
|
||||||
|
for (var k=0; k<n; k++) {
|
||||||
|
mid[k] <== b[k]*c[k];
|
||||||
|
out[k] <== a[k] * (b[k]+c[k]-2*mid[k]) + mid[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
10
circuits/sha256/rotate.circom
Normal file
10
circuits/sha256/rotate.circom
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
template RotR(n, r) {
|
||||||
|
signal input in[n];
|
||||||
|
signal output out[n];
|
||||||
|
|
||||||
|
for (var i=0; i<n; i++) {
|
||||||
|
out[i] <== in[ (i+r)%n ];
|
||||||
|
}
|
||||||
|
}
|
||||||
49
circuits/sha256/sha256_2.circom
Normal file
49
circuits/sha256/sha256_2.circom
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
include "sha256compression.circom";
|
||||||
|
include "bitify.circom"
|
||||||
|
|
||||||
|
template Sha256_2() {
|
||||||
|
signal input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
component bits2num = Bits2Num(216);
|
||||||
|
component num2bits[2];
|
||||||
|
|
||||||
|
num2bits[0] = Num2Bits(216);
|
||||||
|
num2bits[1] = Num2Bits(216);
|
||||||
|
|
||||||
|
num2bits[0].in <== a;
|
||||||
|
num2bits[1].in <== b;
|
||||||
|
|
||||||
|
component sha256compression = Sha256compression() ;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
for (i=0; i<216; i++) {
|
||||||
|
sha256compression.inp[i] <== num2bits[0].out[215-i];
|
||||||
|
sha256compression.inp[i+216] <== num2bits[1].out[215-i];
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256compression.inp[432] <== 1;
|
||||||
|
|
||||||
|
for (i=433; i<503; i++) {
|
||||||
|
sha256compression.inp[i] <== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256compression.inp[503] <== 1;
|
||||||
|
sha256compression.inp[504] <== 1;
|
||||||
|
sha256compression.inp[505] <== 0;
|
||||||
|
sha256compression.inp[506] <== 1;
|
||||||
|
sha256compression.inp[507] <== 1;
|
||||||
|
sha256compression.inp[508] <== 0;
|
||||||
|
sha256compression.inp[509] <== 0;
|
||||||
|
sha256compression.inp[510] <== 0;
|
||||||
|
sha256compression.inp[511] <== 0;
|
||||||
|
|
||||||
|
for (i=0; i<216; i++) {
|
||||||
|
bits2num.in[i] <== sha256compression.out[255-i];
|
||||||
|
}
|
||||||
|
|
||||||
|
out <== bits2num.out;
|
||||||
|
}
|
||||||
146
circuits/sha256/sha256compression.circom
Normal file
146
circuits/sha256/sha256compression.circom
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
|
||||||
|
include "constants.circom";
|
||||||
|
include "t1.circom";
|
||||||
|
include "t2.circom";
|
||||||
|
include "binsum.circom";
|
||||||
|
include "sigmaplus.circom";
|
||||||
|
|
||||||
|
template Sha256compression() {
|
||||||
|
signal input inp[512];
|
||||||
|
signal output out[256];
|
||||||
|
signal a[65][32];
|
||||||
|
signal b[65][32];
|
||||||
|
signal c[65][32];
|
||||||
|
signal d[65][32];
|
||||||
|
signal e[65][32];
|
||||||
|
signal f[65][32];
|
||||||
|
signal g[65][32];
|
||||||
|
signal h[65][32];
|
||||||
|
signal w[64][32];
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
component sigmaPlus[48];
|
||||||
|
for (i=0; i<48; i++) sigmaPlus[i] = SigmaPlus();
|
||||||
|
|
||||||
|
component ct_k[64];
|
||||||
|
for (i=0; i<64; i++) ct_k[i] = K(i);
|
||||||
|
|
||||||
|
component ha0 = H(0);
|
||||||
|
component hb0 = H(1);
|
||||||
|
component hc0 = H(2);
|
||||||
|
component hd0 = H(3);
|
||||||
|
component he0 = H(4);
|
||||||
|
component hf0 = H(5);
|
||||||
|
component hg0 = H(6);
|
||||||
|
component hh0 = H(7);
|
||||||
|
|
||||||
|
component t1[64];
|
||||||
|
for (i=0; i<64; i++) t1[i] = T1();
|
||||||
|
|
||||||
|
component t2[64];
|
||||||
|
for (i=0; i<64; i++) t2[i] = T2();
|
||||||
|
|
||||||
|
component suma[64];
|
||||||
|
for (i=0; i<64; i++) suma[i] = BinSum(32, 2);
|
||||||
|
|
||||||
|
component sume[64];
|
||||||
|
for (i=0; i<64; i++) sume[i] = BinSum(32, 2);
|
||||||
|
|
||||||
|
component fsum[8];
|
||||||
|
for (i=0; i<8; i++) fsum[i] = BinSum(32, 2);
|
||||||
|
|
||||||
|
var k;
|
||||||
|
var t;
|
||||||
|
|
||||||
|
for (t=0; t<64; t++) {
|
||||||
|
if (t<16) {
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
w[t][k] <== inp[t*32+31-k];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
sigmaPlus[t-16].in2[k] <== w[t-2][k];
|
||||||
|
sigmaPlus[t-16].in7[k] <== w[t-7][k];
|
||||||
|
sigmaPlus[t-16].in15[k] <== w[t-15][k];
|
||||||
|
sigmaPlus[t-16].in16[k] <== w[t-16][k];
|
||||||
|
w[t][k] <== sigmaPlus[t-16].out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<32; k++ ) {
|
||||||
|
a[0][k] <== ha0.out[k]
|
||||||
|
b[0][k] <== hb0.out[k]
|
||||||
|
c[0][k] <== hc0.out[k]
|
||||||
|
d[0][k] <== hd0.out[k]
|
||||||
|
e[0][k] <== he0.out[k]
|
||||||
|
f[0][k] <== hf0.out[k]
|
||||||
|
g[0][k] <== hg0.out[k]
|
||||||
|
h[0][k] <== hh0.out[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
for (t = 0; t<64; t++) {
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
t1[t].h[k] <== h[t][k];
|
||||||
|
t1[t].e[k] <== e[t][k];
|
||||||
|
t1[t].f[k] <== f[t][k];
|
||||||
|
t1[t].g[k] <== g[t][k];
|
||||||
|
t1[t].k[k] <== ct_k[t].out[k];
|
||||||
|
t1[t].w[k] <== w[t][k];
|
||||||
|
|
||||||
|
t2[t].a[k] <== a[t][k];
|
||||||
|
t2[t].b[k] <== b[t][k];
|
||||||
|
t2[t].c[k] <== c[t][k];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
sume[t].in[0][k] <== d[t][k];
|
||||||
|
sume[t].in[1][k] <== t1[t].out[k];
|
||||||
|
|
||||||
|
suma[t].in[0][k] <== t1[t].out[k];
|
||||||
|
suma[t].in[1][k] <== t2[t].out[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
h[t+1][k] <== g[t][k];
|
||||||
|
g[t+1][k] <== f[t][k];
|
||||||
|
f[t+1][k] <== e[t][k];
|
||||||
|
e[t+1][k] <== sume[t].out[k];
|
||||||
|
d[t+1][k] <== c[t][k];
|
||||||
|
c[t+1][k] <== b[t][k];
|
||||||
|
b[t+1][k] <== a[t][k];
|
||||||
|
a[t+1][k] <== suma[t].out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
fsum[0].in[0][k] <== ha0.out[k];
|
||||||
|
fsum[0].in[1][k] <== a[64][k];
|
||||||
|
fsum[1].in[0][k] <== hb0.out[k];
|
||||||
|
fsum[1].in[1][k] <== b[64][k];
|
||||||
|
fsum[2].in[0][k] <== hc0.out[k];
|
||||||
|
fsum[2].in[1][k] <== c[64][k];
|
||||||
|
fsum[3].in[0][k] <== hd0.out[k];
|
||||||
|
fsum[3].in[1][k] <== d[64][k];
|
||||||
|
fsum[4].in[0][k] <== he0.out[k];
|
||||||
|
fsum[4].in[1][k] <== e[64][k];
|
||||||
|
fsum[5].in[0][k] <== hf0.out[k];
|
||||||
|
fsum[5].in[1][k] <== f[64][k];
|
||||||
|
fsum[6].in[0][k] <== hg0.out[k];
|
||||||
|
fsum[6].in[1][k] <== g[64][k];
|
||||||
|
fsum[7].in[0][k] <== hh0.out[k];
|
||||||
|
fsum[7].in[1][k] <== h[64][k];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k=0; k<32; k++) {
|
||||||
|
out[31-k] <== fsum[0].out[k];
|
||||||
|
out[32+31-k] <== fsum[1].out[k];
|
||||||
|
out[64+31-k] <== fsum[2].out[k];
|
||||||
|
out[96+31-k] <== fsum[3].out[k];
|
||||||
|
out[128+31-k] <== fsum[4].out[k];
|
||||||
|
out[160+31-k] <== fsum[5].out[k];
|
||||||
|
out[192+31-k] <== fsum[6].out[k];
|
||||||
|
out[224+31-k] <== fsum[7].out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
14
circuits/sha256/shift.circom
Normal file
14
circuits/sha256/shift.circom
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
template ShR(n, r) {
|
||||||
|
signal input in[n];
|
||||||
|
signal output out[n];
|
||||||
|
|
||||||
|
for (var i=0; i<n; i++) {
|
||||||
|
if (i+r >= n) {
|
||||||
|
out[i] <== 0;
|
||||||
|
} else {
|
||||||
|
out[i] <== in[ i+r ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
49
circuits/sha256/sigma.circom
Normal file
49
circuits/sha256/sigma.circom
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
include "xor3.circom";
|
||||||
|
include "rotate.circom";
|
||||||
|
include "shift.circom";
|
||||||
|
|
||||||
|
template SmallSigma(ra, rb, rc) {
|
||||||
|
signal input in[32];
|
||||||
|
signal output out[32];
|
||||||
|
|
||||||
|
component xor3 = Xor3(32);
|
||||||
|
|
||||||
|
component rota = RotR(32, ra);
|
||||||
|
component rotb = RotR(32, rb);
|
||||||
|
component shrc = ShR(32, rc);
|
||||||
|
|
||||||
|
for (var k=0; k<32; k++) {
|
||||||
|
rota.in[k] <== in[k];
|
||||||
|
rotb.in[k] <== in[k];
|
||||||
|
shrc.in[k] <== in[k];
|
||||||
|
|
||||||
|
xor3.a[k] <== rota.out[k];
|
||||||
|
xor3.b[k] <== rotb.out[k];
|
||||||
|
xor3.c[k] <== shrc.out[k];
|
||||||
|
|
||||||
|
out[k] <== xor3.out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template BigSigma(ra, rb, rc) {
|
||||||
|
signal input in[32];
|
||||||
|
signal output out[32];
|
||||||
|
|
||||||
|
component xor3 = Xor3(32);
|
||||||
|
|
||||||
|
component rota = RotR(32, ra);
|
||||||
|
component rotb = RotR(32, rb);
|
||||||
|
component rotc = RotR(32, rc);
|
||||||
|
|
||||||
|
for (var k=0; k<32; k++) {
|
||||||
|
rota.in[k] <== in[k];
|
||||||
|
rotb.in[k] <== in[k];
|
||||||
|
rotc.in[k] <== in[k];
|
||||||
|
|
||||||
|
xor3.a[k] <== rota.out[k];
|
||||||
|
xor3.b[k] <== rotb.out[k];
|
||||||
|
xor3.c[k] <== rotc.out[k];
|
||||||
|
|
||||||
|
out[k] <== xor3.out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
26
circuits/sha256/sigmaplus.circom
Normal file
26
circuits/sha256/sigmaplus.circom
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
include "binsum.circom"
|
||||||
|
include "sigma.circom"
|
||||||
|
|
||||||
|
template SigmaPlus() {
|
||||||
|
signal input in2[32];
|
||||||
|
signal input in7[32];
|
||||||
|
signal input in15[32];
|
||||||
|
signal input in16[32];
|
||||||
|
signal output out[32];
|
||||||
|
|
||||||
|
component sum = BinSum(32, 4);
|
||||||
|
component sigma1 = SmallSigma(17,19,10);
|
||||||
|
component sigma0 = SmallSigma(7, 18, 3);
|
||||||
|
|
||||||
|
for (var k=0; k<32; k++) {
|
||||||
|
sigma1.in[k] <== in2[k];
|
||||||
|
sigma0.in[k] <== in15[k];
|
||||||
|
|
||||||
|
sum.in[0][k] <== sigma1.out[k];
|
||||||
|
sum.in[1][k] <== in7[k];
|
||||||
|
sum.in[2][k] <== sigma0.out[k];
|
||||||
|
sum.in[3][k] <== in16[k];
|
||||||
|
|
||||||
|
out[k] <== sum.out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
33
circuits/sha256/t1.circom
Normal file
33
circuits/sha256/t1.circom
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
include "binsum.circom";
|
||||||
|
include "sigma.circom";
|
||||||
|
include "ch.circom";
|
||||||
|
|
||||||
|
template T1() {
|
||||||
|
signal input h[32];
|
||||||
|
signal input e[32];
|
||||||
|
signal input f[32];
|
||||||
|
signal input g[32];
|
||||||
|
signal input k[32];
|
||||||
|
signal input w[32];
|
||||||
|
signal output out[32];
|
||||||
|
|
||||||
|
component sum = BinSum(32, 5);
|
||||||
|
component ch = Ch(32);
|
||||||
|
|
||||||
|
component bigsigma1 = BigSigma(6, 11, 25);
|
||||||
|
|
||||||
|
for (var ki=0; ki<32; ki++) {
|
||||||
|
bigsigma1.in[ki] <== e[ki];
|
||||||
|
ch.a[ki] <== e[ki];
|
||||||
|
ch.b[ki] <== f[ki];
|
||||||
|
ch.c[ki] <== g[ki]
|
||||||
|
|
||||||
|
sum.in[0][ki] <== h[ki];
|
||||||
|
sum.in[1][ki] <== bigsigma1.out[ki];
|
||||||
|
sum.in[2][ki] <== ch.out[ki];
|
||||||
|
sum.in[3][ki] <== k[ki];
|
||||||
|
sum.in[4][ki] <== w[ki];
|
||||||
|
|
||||||
|
out[ki] <== sum.out[ki];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
circuits/sha256/t2.circom
Normal file
28
circuits/sha256/t2.circom
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
include "binsum.circom";
|
||||||
|
include "sigma.circom";
|
||||||
|
include "maj.circom"
|
||||||
|
|
||||||
|
template T2() {
|
||||||
|
signal input a[32];
|
||||||
|
signal input b[32];
|
||||||
|
signal input c[32];
|
||||||
|
signal output out[32];
|
||||||
|
|
||||||
|
component sum = BinSum(32, 2);
|
||||||
|
|
||||||
|
component bigsigma0 = BigSigma(2, 13, 22);
|
||||||
|
component maj = Maj(32);
|
||||||
|
|
||||||
|
for (var k=0; k<32; k++) {
|
||||||
|
|
||||||
|
bigsigma0.in[k] <== a[k];
|
||||||
|
maj.a[k] <== a[k];
|
||||||
|
maj.b[k] <== b[k];
|
||||||
|
maj.c[k] <== c[k];
|
||||||
|
|
||||||
|
sum.in[0][k] <== bigsigma0.out[k];
|
||||||
|
sum.in[1][k] <== maj.out[k];
|
||||||
|
|
||||||
|
out[k] <== sum.out[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
25
circuits/sha256/xor3.circom
Normal file
25
circuits/sha256/xor3.circom
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* Xor3 function for sha256
|
||||||
|
|
||||||
|
out = a ^ b ^ c =>
|
||||||
|
|
||||||
|
out = a+b+c - 2*a*b - 2*a*c - 2*b*c + 4*a*b*c =>
|
||||||
|
|
||||||
|
out = a*( 1 - 2*b - 2*c + 4*b*c ) + b + c - 2*b*c =>
|
||||||
|
|
||||||
|
mid = b*c
|
||||||
|
out = a*( 1 - 2*b -2*c + 4*mid ) + b + c - 2 * mid
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template Xor3(n) {
|
||||||
|
signal input a[n];
|
||||||
|
signal input b[n];
|
||||||
|
signal input c[n];
|
||||||
|
signal output out[n];
|
||||||
|
signal mid[n];
|
||||||
|
|
||||||
|
for (var k=0; k<n; k++) {
|
||||||
|
mid[k] <== b[k]*c[k];
|
||||||
|
out[k] <== a[k] * (1 -2*b[k] -2*c[k] +4*mid[k]) + b[k] + c[k] -2*mid[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
20
circuits/tobin.circom
Normal file
20
circuits/tobin.circom
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template toBin(n) {
|
||||||
|
signal input inp;
|
||||||
|
signal output out[n];
|
||||||
|
var lc1=0;
|
||||||
|
|
||||||
|
for (var i = 0; i<n; i++) {
|
||||||
|
out[i] <-- (inp >> i) & 1;
|
||||||
|
out[i] * (out[i] -1 ) === 0;
|
||||||
|
lc1 += out[i] * 2**i;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc1 === inp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
component toBin(3) main;
|
||||||
132
cli.js
132
cli.js
@@ -3,29 +3,26 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 0KIMS association.
|
Copyright 2018 0KIMS association.
|
||||||
|
|
||||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||||
|
|
||||||
circom is a free software: you can redistribute it and/or modify it
|
jaz is a free software: you can redistribute it and/or modify it
|
||||||
under the terms of the GNU General Public License as published by
|
under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
License for more details.
|
License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
|
||||||
const stringifyBigInts = require("ffjavascript").utils.stringifyBigInts;
|
|
||||||
const fastFile = require("fastfile");
|
|
||||||
|
|
||||||
const compiler = require("./src/compiler");
|
const compiler = require("./src/compiler");
|
||||||
|
|
||||||
@@ -33,27 +30,13 @@ const version = require("./package").version;
|
|||||||
|
|
||||||
const argv = require("yargs")
|
const argv = require("yargs")
|
||||||
.version(version)
|
.version(version)
|
||||||
.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]")
|
.usage("circom -s [input source circuit file] -o [output definition circuit file]")
|
||||||
|
.alias("s", "source")
|
||||||
.alias("o", "output")
|
.alias("o", "output")
|
||||||
.alias("c", "csource")
|
.alias("c", "cfile")
|
||||||
.alias("w", "wasm")
|
.require(["s","o"])
|
||||||
.alias("t", "wat")
|
|
||||||
.alias("s", "sym")
|
|
||||||
.alias("r", "r1cs")
|
|
||||||
.alias("p", "prime")
|
|
||||||
.alias("n", "newThreadTemplates")
|
|
||||||
.help("h")
|
.help("h")
|
||||||
.alias("h", "help")
|
.alias("h", "help")
|
||||||
.option("verbose", {
|
|
||||||
alias: "v",
|
|
||||||
type: "boolean",
|
|
||||||
description: "Run with verbose logging"
|
|
||||||
})
|
|
||||||
.option("fast", {
|
|
||||||
alias: "f",
|
|
||||||
type: "boolean",
|
|
||||||
description: "Do not optimize constraints"
|
|
||||||
})
|
|
||||||
.epilogue(`Copyright (C) 2018 0kims association
|
.epilogue(`Copyright (C) 2018 0kims association
|
||||||
This program comes with ABSOLUTELY NO WARRANTY;
|
This program comes with ABSOLUTELY NO WARRANTY;
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
@@ -61,102 +44,17 @@ const argv = require("yargs")
|
|||||||
repo directory at https://github.com/iden3/circom `)
|
repo directory at https://github.com/iden3/circom `)
|
||||||
.argv;
|
.argv;
|
||||||
|
|
||||||
|
const fullFileName = path.resolve(process.cwd(), argv.source);
|
||||||
|
|
||||||
async function run() {
|
compiler(fullFileName, argv.cfile).then( (cir) => {
|
||||||
let inputFile;
|
fs.writeFileSync(argv.output, JSON.stringify(cir, null, 1), "utf8");
|
||||||
if (argv._.length == 0) {
|
|
||||||
inputFile = "circuit.circom";
|
|
||||||
} else if (argv._.length == 1) {
|
|
||||||
inputFile = argv._[0];
|
|
||||||
} else {
|
|
||||||
console.log("Only one circuit at a time is permited");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullFileName = path.resolve(process.cwd(), inputFile);
|
|
||||||
const fileName = path.basename(fullFileName, ".circom");
|
|
||||||
const cSourceName = typeof(argv.csource) === "string" ? argv.csource : fileName + ".cpp";
|
|
||||||
const wasmName = typeof(argv.wasm) === "string" ? argv.wasm : fileName + ".wasm";
|
|
||||||
const watName = typeof(argv.wat) === "string" ? argv.wat : fileName + ".wat";
|
|
||||||
const r1csName = typeof(argv.r1cs) === "string" ? argv.r1cs : fileName + ".r1cs";
|
|
||||||
const symName = typeof(argv.sym) === "string" ? argv.sym : fileName + ".sym";
|
|
||||||
|
|
||||||
const options = {};
|
|
||||||
options.reduceConstraints = !argv.fast;
|
|
||||||
options.verbose = argv.verbose || false;
|
|
||||||
options.sanityCheck = argv.sanitycheck;
|
|
||||||
|
|
||||||
if (argv.csource) {
|
|
||||||
options.cSourceFile = await fastFile.createOverride(cSourceName);
|
|
||||||
const noExt = cSourceName.substr(0, cSourceName.lastIndexOf(".")) || cSourceName;
|
|
||||||
options.dataFile = await fastFile.createOverride(noExt+".dat");
|
|
||||||
}
|
|
||||||
if (argv.wasm) {
|
|
||||||
options.wasmFile = await fastFile.createOverride(wasmName);
|
|
||||||
}
|
|
||||||
if (argv.wat) {
|
|
||||||
options.watFile = await fastFile.createOverride(watName);
|
|
||||||
}
|
|
||||||
if (argv.r1cs) {
|
|
||||||
options.r1csFileName = r1csName;
|
|
||||||
}
|
|
||||||
if (argv.sym) {
|
|
||||||
options.symWriteStream = fs.createWriteStream(symName);
|
|
||||||
}
|
|
||||||
if (argv.newThreadTemplates) {
|
|
||||||
options.newThreadTemplates = new RegExp(argv.newThreadTemplates);
|
|
||||||
}
|
|
||||||
if (!argv.prime) {
|
|
||||||
options.prime = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
||||||
} else if (["BLS12-381", "BLS12381"]. indexOf(argv.prime.toUpperCase()) >=0) {
|
|
||||||
options.prime = Scalar.fromString("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001",16);
|
|
||||||
} else if (["BN-128", "BN128", "BN254", "BN-254"]. indexOf(argv.prime.toUpperCase()) >=0) {
|
|
||||||
options.prime = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
||||||
} else {
|
|
||||||
options.prime = Scalar.fromString(argv.prime);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await compiler(fullFileName, options);
|
|
||||||
|
|
||||||
if (options.cSourceFile) await options.cSourceFile.close();
|
|
||||||
if (options.dataFile) await options.dataFile.close();
|
|
||||||
if (options.wasmFile) await options.wasmFile.close();
|
|
||||||
if (options.watFile) await options.watFile.close();
|
|
||||||
let symDone = false;
|
|
||||||
if (options.symWriteStream) {
|
|
||||||
options.symWriteStream.on("finish", () => {
|
|
||||||
symDone = true;
|
|
||||||
finishIfDone();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
symDone = true;
|
|
||||||
}
|
|
||||||
function finishIfDone() {
|
|
||||||
if (symDone) {
|
|
||||||
setTimeout(() => {
|
|
||||||
process.exit(0);
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
run().then(()=> {
|
|
||||||
process.exit(0);
|
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
// console.log(err);
|
console.log(err);
|
||||||
console.log(err.stack);
|
|
||||||
if (err.pos) {
|
|
||||||
console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`);
|
console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`);
|
||||||
} else {
|
console.error(JSON.stringify(err.ast, null, 1));
|
||||||
console.log(err.message);
|
|
||||||
if (argv.verbose) console.log(err.stack);
|
|
||||||
}
|
|
||||||
if (err.ast) {
|
|
||||||
console.error(JSON.stringify(stringifyBigInts(err.ast), null, 1));
|
|
||||||
}
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,654 +0,0 @@
|
|||||||
# Binary format for R1CS
|
|
||||||
|
|
||||||
---
|
|
||||||
eip:
|
|
||||||
title: r1cs binary format
|
|
||||||
author: Jordi Baylina <jordi@baylina.cat>
|
|
||||||
discussions-to:
|
|
||||||
status: draft
|
|
||||||
type: Standards Track
|
|
||||||
category: ERC
|
|
||||||
created: 2019-09-24
|
|
||||||
requires:
|
|
||||||
---
|
|
||||||
|
|
||||||
## Simple Summary
|
|
||||||
|
|
||||||
This standard defines a standard format for a binery representation of a r1cs constraint system.
|
|
||||||
|
|
||||||
## Abstract
|
|
||||||
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
The zero knowledge primitives, requires the definition of a statment that wants to be proved. This statment can be expressed as a deterministric program or an algebraic circuit. Lots of primitives like zkSnarks, bulletProofs or aurora, requires to convert this statment to a rank-one constraint system.
|
|
||||||
|
|
||||||
This standard specifies a format for a r1cs and allows the to connect a set of tools that compiles a program or a circuit to r1cs that can be used for the zksnarks or bulletproofs primitives.
|
|
||||||
|
|
||||||
## Specification
|
|
||||||
|
|
||||||
### General considerations
|
|
||||||
|
|
||||||
The standard extension is `.r1cs`
|
|
||||||
|
|
||||||
|
|
||||||
A deterministic program (or circuit) is a program that generates a set of deterministic values given an input. All those values are labeled from l_{0} to l_{n_labels}
|
|
||||||
|
|
||||||
This file defines a map beween l_{i} -> w_{j} and defines a series a R1CS of the form
|
|
||||||
|
|
||||||
$$
|
|
||||||
\left\{ \begin{array}{rclclcl}
|
|
||||||
(a_{0,0}w_0 + a_{0,1}w_1 + ... + a_{0,n}w_{n}) &\cdot& (b_{0,0} w_0 + b_{0,1} w_1 + ... + b_{0,n} w_{n}) &-& (c_{0,0} w_0 + c_{0,1} w_1 + ... + c_{0,n}w_{n}) &=& 0 \\
|
|
||||||
(a_{1,0}w_0 + a_{1,1}w_1 + ... + a_{1,n}w_{n}) &\cdot& (b_{1,0} w_0 + b_{1,1} w_1 + ... + b_{1,n} w_{n}) &-& (c_{1,0} w_0 + c_{1,1}w_1 + ... + c_{1,n}w_{n}) &=& 0 \\
|
|
||||||
...\\
|
|
||||||
(a_{m-1,0}w_0 + a_{m-1,1}w_1 + ... + a_{m-1,n}w_{n}) &\cdot& (b_{m-1,0} w_0 + b_{m-1,1} w_1 + ... + b_{m-1,n} w_{n}) &-& (c_{m-1,0} w_0 + c_{m-1,1}w_1 + ... + c_{m-1,n}w_{n}) &=& 0
|
|
||||||
\end{array} \right.
|
|
||||||
$$
|
|
||||||
|
|
||||||
Wire 0 must be always mapped to label 0 and it's an input forced to value "1" implicitly
|
|
||||||
|
|
||||||
|
|
||||||
### Format of the file
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ 72 31 63 73 ┃ Magic "r1cs"
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ 01 00 00 00 ┃ Version 1
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ 03 00 00 00 ┃ Number of Sections
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ sectionType ┃ 8 │ SectionSize ┃
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ Section Content ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┗━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ sectionType ┃ 8 │ SectionSize ┃
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ Section Content ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┃ ┃
|
|
||||||
┗━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
...
|
|
||||||
...
|
|
||||||
...
|
|
||||||
````
|
|
||||||
|
|
||||||
#### Magic Number
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
The file start with a constant 4 bytes (magic number) "r1cs"
|
|
||||||
|
|
||||||
```
|
|
||||||
0x72 0x31 0x63 0x73
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Version
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
For this standard it's fixed to
|
|
||||||
|
|
||||||
```
|
|
||||||
0x01 0x00 0x00 0x00
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Number of Sections
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Number of sections contained in the file
|
|
||||||
|
|
||||||
#### SectionType
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Type of the section.
|
|
||||||
|
|
||||||
Currently there are 3 types of sections defined:
|
|
||||||
|
|
||||||
* 0x00000001 : Header Section
|
|
||||||
* 0x00000002 : Constraint Section
|
|
||||||
* 0x00000003 : Wire2LabelId Map Section
|
|
||||||
|
|
||||||
If the file contain other types, the format is valid, but they MUST be ignored.
|
|
||||||
|
|
||||||
Any order of the section must be accepted.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
0x01 0x00 0x00 0x00
|
|
||||||
```
|
|
||||||
|
|
||||||
#### SectionSize
|
|
||||||
|
|
||||||
Size: `ws` bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Size in bytes of the section
|
|
||||||
|
|
||||||
### Header Section
|
|
||||||
|
|
||||||
Section Type: 0x01
|
|
||||||
````
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 4 │ 20 00 00 00 ┃ Field Size in bytes (fs)
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ fs │ 010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430 ┃ Prime size
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 32 │ 01 00 00 00 ┃ nWires
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 32 │ 01 00 00 00 ┃ nPubOut
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 32 │ 01 00 00 00 ┃ nPubIn
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 32 │ 01 00 00 00 ┃ nPrvIn
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 64 │ 01 00 00 00 00 00 00 00 ┃ nLabels
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 32 │ 01 00 00 00 ┃ mConstraints
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
#### field Size (fs)
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Size in bytes of a field element. Mast be a multiple of 8.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
0x00 0x0 0x00 0x00
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Prime
|
|
||||||
|
|
||||||
Prime Number of the field
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
0x010000f0_93f5e143_9170b979_48e83328_5d588181_b64550b8_29a031e1_724e6430
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Number of wires
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of wires including ONE signal (Index 0).
|
|
||||||
|
|
||||||
#### Number of public outputs
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of wires public output wires. They should be starting at idx 1
|
|
||||||
|
|
||||||
#### Number of public inputs
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of wires public input wires. They should be starting just after the public output
|
|
||||||
|
|
||||||
#### Number of private inputs
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of wires private input wires. They should be starting just after the public inputs
|
|
||||||
|
|
||||||
#### Number of Labels
|
|
||||||
|
|
||||||
Size: 8 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of wires private input wires. They should be starting just after the public inputs
|
|
||||||
|
|
||||||
#### Number of constraints (m)
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total Number of constraints
|
|
||||||
|
|
||||||
### Constraints section
|
|
||||||
|
|
||||||
Section Type: 0x02
|
|
||||||
|
|
||||||
````
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ nA ┃ ╲
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ a_{0,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ a_{0,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nA ┃ fs │ a_{0,wireId_nA} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nB ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ b_{0,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ b_{0,wireId_2} ┃ ╲
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_0
|
|
||||||
... ... ╱
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nB ┃ fs │ b_{0,wireId_nB} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nC ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ c_{0,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ c_{0,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nC ┃ fs │ c_{0,wireId_nC} ┃ ╱
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
|
||||||
╱
|
|
||||||
|
|
||||||
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ nA ┃ ╲
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ a_{1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ a_{1,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nA ┃ fs │ a_{1,wireId_nA} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nB ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ b_{1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ b_{1,wireId_2} ┃ ╲
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_1
|
|
||||||
... ... ╱
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nB ┃ fs │ b_{1,wireId_nB} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nC ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ c_{1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ c_{1,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nC ┃ fs │ c_{1,wireId_nC} ┃ ╱
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
|
||||||
╱
|
|
||||||
|
|
||||||
...
|
|
||||||
...
|
|
||||||
...
|
|
||||||
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ nA ┃ ╲
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ ╲
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ a_{m-1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ a_{m-1,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nA ┃ fs │ a_{m-1,wireId_nA} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nB ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ b_{m-1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ ╲
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ b_{m-1,wireId_2} ┃ ╲
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱ Constraint_{m-1}
|
|
||||||
... ... ╱
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nB ┃ fs │ b_{m-1,wireId_nB} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ nC ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_1 ┃ fs │ c_{m-1,wireId_1} ┃ │
|
|
||||||
┣━━━━╋━━━━━━━━━━━━━━━━━╋━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━┫ │
|
|
||||||
┃ 32 │ wireId_2 ┃ fs │ c_{m-1,wireId_2} ┃ │
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ │
|
|
||||||
... ... │
|
|
||||||
┏━━━━┳━━━━━━━━━━━━━━━━━┳━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ │
|
|
||||||
┃ 32 │ wireId_nC ┃ fs │ c_{m-1,wireId_nC} ┃ ╱
|
|
||||||
┗━━━━┻━━━━━━━━━━━━━━━━━┻━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━┛ ╱
|
|
||||||
╱
|
|
||||||
````
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Constraints
|
|
||||||
|
|
||||||
Each constraint contains 3 linear combinations A, B, C.
|
|
||||||
|
|
||||||
The constraint is such that:
|
|
||||||
```
|
|
||||||
A*B-C = 0
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Linear combination
|
|
||||||
|
|
||||||
Each linear combination is of the form:
|
|
||||||
|
|
||||||
$$
|
|
||||||
a_{j,0}w_0 + a_{j,1}w_1 + ... + a_{j,n}w_{n}
|
|
||||||
$$
|
|
||||||
|
|
||||||
#### Number of nonZero Factors
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
Total number of non Zero factors in the linear compination.
|
|
||||||
|
|
||||||
The factors MUST be sorted in ascending order.
|
|
||||||
|
|
||||||
#### Factor
|
|
||||||
|
|
||||||
For each factor we have the index of the factor and the value of the factor.
|
|
||||||
|
|
||||||
#### WireId of the factor
|
|
||||||
|
|
||||||
Size: 4 bytes
|
|
||||||
Format: Little-Endian
|
|
||||||
|
|
||||||
WireId of the nonZero Factor
|
|
||||||
|
|
||||||
#### Value of the factor
|
|
||||||
|
|
||||||
This is the factor that multiplies the associated wire in the linear convination.
|
|
||||||
|
|
||||||
For example, to represent the linear combination:
|
|
||||||
|
|
||||||
$$
|
|
||||||
5w_4 +8w_5 + 260w_{886}
|
|
||||||
$$
|
|
||||||
|
|
||||||
The linear combination would be represented as:
|
|
||||||
|
|
||||||
````
|
|
||||||
┏━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 03 00 00 00 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 04 00 00 00 ┃ 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 05 00 00 00 ┃ 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 76 03 00 00 ┃ 04010000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
````
|
|
||||||
|
|
||||||
|
|
||||||
### WireId2LabelId Map Section
|
|
||||||
|
|
||||||
Section Type: 0x03
|
|
||||||
|
|
||||||
````
|
|
||||||
┏━━┳━━━━━━━━━━━━━━━━━━━┳━━┳━━━━━━━━━━━━━━━━━━━┓ ┏━━┳━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃64│ labelId of Wire_0 ┃64│ labelId of Wire_1 ┃ ... ┃64│ labelId of Wire_n ┃
|
|
||||||
┗━━┻━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━━━━━━━━━━━━━━━┛ ┗━━┻━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
````
|
|
||||||
|
|
||||||
## Rationale
|
|
||||||
|
|
||||||
Variable size for field elements allows to shrink the size of the file and allows to work with any field.
|
|
||||||
|
|
||||||
Version allows to update the format.
|
|
||||||
|
|
||||||
Have a very good comprasion ratio for sparse r1cs as it's the normal case.
|
|
||||||
|
|
||||||
The motivation of having a map between l and w is that this allows optimizers to calculate equivalent r1cs systems but keeping the original values geneated by the circuit.
|
|
||||||
|
|
||||||
|
|
||||||
## Backward Compatibility
|
|
||||||
|
|
||||||
N.A.
|
|
||||||
|
|
||||||
## Test Cases
|
|
||||||
### Example
|
|
||||||
|
|
||||||
Given this r1cs in a 256 bit Field:
|
|
||||||
|
|
||||||
|
|
||||||
$$
|
|
||||||
\left\{ \begin{array}{rclclcl}
|
|
||||||
(3w_5 + 8w_6) &\cdot& (2w_0 + 20w_2 + 12w_3) &-& (5w_0 + 7w_2) &=& 0 \\
|
|
||||||
(4w_1 + 8w_4 + 3w_5) &\cdot& (6w_6 + 44w_3) && &=& 0 \\
|
|
||||||
(4w_6) &\cdot& (6w_0 + 5w_3 + 11s_2) &-& (600w_6) &=& 0
|
|
||||||
\end{array} \right.
|
|
||||||
$$
|
|
||||||
|
|
||||||
And a Wire to label map.
|
|
||||||
|
|
||||||
$$
|
|
||||||
w_0 := l_0 \\
|
|
||||||
w_1 := l_3 \\
|
|
||||||
w_2 := l_{10} \\
|
|
||||||
w_3 := l_{11} \\
|
|
||||||
w_4 := l_{12} \\
|
|
||||||
w_5 := l_{15} \\
|
|
||||||
w_6 := l_{324} \\
|
|
||||||
$$
|
|
||||||
|
|
||||||
The format will be:
|
|
||||||
|
|
||||||
````
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 72316377 ┃ Magic
|
|
||||||
┣━━━━━━━━━━┫
|
|
||||||
┃ 01000000 ┃ Version
|
|
||||||
┣━━━━━━━━━━┫
|
|
||||||
┃ 03000000 ┃ nSections
|
|
||||||
┗━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 01000000 ┃ 40000000 00000000 ┃ SectionType: Header
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 20000000 ┃ Field Size
|
|
||||||
┣━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430 ┃
|
|
||||||
┣━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┃ 07000000 ┃ # of wires
|
|
||||||
┣━━━━━━━━━━┫
|
|
||||||
┃ 01000000 ┃ # Public Outs
|
|
||||||
┣━━━━━━━━━━┫
|
|
||||||
┃ 02000000 ┃ # Public Ins
|
|
||||||
┣━━━━━━━━━━┫
|
|
||||||
┃ 03000000 ┃ # Private Ins
|
|
||||||
┣━━━━━━━━━━┻━━━━━━━━┓
|
|
||||||
┃ e8030000 00000000 ┃ # Labels
|
|
||||||
┣━━━━━━━━━━┳━━━━━━━━┛
|
|
||||||
┃ 03000000 ┃ # Constraints
|
|
||||||
┗━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 02000000 ┃ 88200000 00000000 ┃ SectionType: Constraints
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓ Constraint 0: (3w_5 + 8w_6) * (2w_0 + 20w_2 + 12w_3) - (5w_0 + 7w_2) = 0
|
|
||||||
┃ 02000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 05000000 ┃ 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 06000000 ┃ 01000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 03000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 00000000 ┃ 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 02000000 ┃ 01140000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 03000000 ┃ 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 02000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 00000000 ┃ 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 02000000 ┃ 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
┏━━━━━━━━━━┓ Constraint 1: (4w_1 + 8w_4 + 3w_5) * (6w_6 + 44w_3) = 0
|
|
||||||
┃ 03000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 01000000 ┃ 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 04000000 ┃ 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 05000000 ┃ 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 02000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 03000000 ┃ 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 06000000 ┃ 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┛
|
|
||||||
|
|
||||||
┏━━━━━━━━━━┓ Constraint 2: (4w_6) * (6w_0 + 5w_3 + 11w_2) - (600w_6) = 0
|
|
||||||
┃ 01000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 06000000 ┃ 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 03000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 00000000 ┃ 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 02000000 ┃ 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 03000000 ┃ 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━┓
|
|
||||||
┃ 01000000 ┃
|
|
||||||
┣━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 06000000 ┃ 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 03000000 ┃ 38000000 00000000 ┃ Wire to Label Map
|
|
||||||
┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
┏━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
┃ 00000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 03000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 0a000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 0b000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 0c000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 0f000000 00000000 ┃
|
|
||||||
┣━━━━━━━━━━━━━━━━━━━┫
|
|
||||||
┃ 44010000 00000000 ┃
|
|
||||||
┗━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
````
|
|
||||||
|
|
||||||
And the binary representation in Hex:
|
|
||||||
|
|
||||||
````
|
|
||||||
72316377
|
|
||||||
01000000
|
|
||||||
03000000
|
|
||||||
01000000 40000000 00000000
|
|
||||||
20000000
|
|
||||||
010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
|
|
||||||
07000000
|
|
||||||
01000000
|
|
||||||
02000000
|
|
||||||
03000000
|
|
||||||
e8030000 00000000
|
|
||||||
03000000
|
|
||||||
02000000 88200000 00000000
|
|
||||||
02000000
|
|
||||||
05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
06000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000
|
|
||||||
00000000 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
02000000 01140000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
02000000
|
|
||||||
00000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
02000000 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000
|
|
||||||
01000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
04000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
02000000
|
|
||||||
03000000 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
06000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
00000000
|
|
||||||
01000000
|
|
||||||
06000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000
|
|
||||||
00000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
02000000 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
01000000
|
|
||||||
06000000 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
|
||||||
03000000 38000000 00000000
|
|
||||||
00000000 00000000
|
|
||||||
03000000 00000000
|
|
||||||
0a000000 00000000
|
|
||||||
0b000000 00000000
|
|
||||||
0c000000 00000000
|
|
||||||
0f000000 00000000
|
|
||||||
44010000 00000000
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
circom will output this format.
|
|
||||||
|
|
||||||
## Copyright
|
|
||||||
|
|
||||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
|
||||||
|
|
||||||
5
index.js
5
index.js
@@ -1,4 +1 @@
|
|||||||
module.exports.compiler = require("./src/compiler.js");
|
module.exports = require("./src/compiler.js");
|
||||||
module.exports.c_tester = require("./ports/c/tester.js");
|
|
||||||
module.exports.wasm_tester = require("./ports/wasm/tester.js");
|
|
||||||
module.exports.tester = module.exports.wasm_tester;
|
|
||||||
|
|||||||
83
out.c
Normal file
83
out.c
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// File: ../../circuits/sha256/bitify.circom
|
||||||
|
function Num2Bits(ctx)
|
||||||
|
{
|
||||||
|
ctx.setVar("lc1", [], "0");
|
||||||
|
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
ctx.setSignal("out", [ctx.getVar("i",[])], bigInt(bigInt(ctx.getVar("i",[])).greater(bigInt(256)) ? 0 : bigInt(ctx.getSignal("in", [])).shr(bigInt(ctx.getVar("i",[]))).and(__MASK__)).and(bigInt("1")).and(__MASK__));
|
||||||
|
ctx.assert(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).mul(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).add(__P__).sub(bigInt("1")).mod(__P__))).mod(__P__), "0");
|
||||||
|
ctx.setVar("lc1", [], bigInt(ctx.getVar("lc1",[])).add(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("i",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("i",[])), __P__))).mod(__P__))).mod(__P__));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.assert(ctx.getVar("lc1",[]), ctx.getSignal("in", []));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Bits2Num(ctx)
|
||||||
|
{
|
||||||
|
ctx.setVar("lc1", [], "0");
|
||||||
|
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
ctx.setVar("lc1", [], bigInt(ctx.getVar("lc1",[])).add(bigInt(bigInt(ctx.getSignal("in", [ctx.getVar("i",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("i",[])), __P__))).mod(__P__))).mod(__P__));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.setSignal("out", [], ctx.getVar("lc1",[]));
|
||||||
|
ctx.assert(ctx.getSignal("out", []), ctx.getVar("lc1",[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// File: ../../circuits/sha256/binsum.circom
|
||||||
|
function nbits(ctx) {
|
||||||
|
ctx.setVar("n", [], "1");
|
||||||
|
ctx.setVar("r", [], "0");
|
||||||
|
while (bigInt(bigInt(ctx.getVar("n",[])).add(__P__).sub(bigInt("1")).mod(__P__)).lt(bigInt(ctx.getVar("a",[]))) ? 1 : 0) {
|
||||||
|
(ctx.setVar("r", [], bigInt(ctx.getVar("r",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__);
|
||||||
|
ctx.setVar("n", [], bigInt(ctx.getVar("n",[])).mul(bigInt("2")).mod(__P__));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.getVar("r",[]);;
|
||||||
|
}
|
||||||
|
function BinSum(ctx)
|
||||||
|
{
|
||||||
|
ctx.setVar("nout", [], ctx.callFunction("nbits", [bigInt(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("n",[])), __P__)).add(__P__).sub(bigInt("1")).mod(__P__)).mul(bigInt(ctx.getVar("ops",[]))).mod(__P__)]));
|
||||||
|
ctx.setVar("lin", [], "0");
|
||||||
|
ctx.setVar("lout", [], "0");
|
||||||
|
for (ctx.setVar("k", [], "0");bigInt(ctx.getVar("k",[])).lt(bigInt(ctx.getVar("n",[]))) ? 1 : 0;(ctx.setVar("k", [], bigInt(ctx.getVar("k",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
for (ctx.setVar("j", [], "0");bigInt(ctx.getVar("j",[])).lt(bigInt(ctx.getVar("ops",[]))) ? 1 : 0;(ctx.setVar("j", [], bigInt(ctx.getVar("j",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
ctx.setVar("lin", [], bigInt(ctx.getVar("lin",[])).add(bigInt(bigInt(ctx.getSignal("in", [ctx.getVar("j",[]),ctx.getVar("k",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("k",[])), __P__))).mod(__P__))).mod(__P__));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ctx.setVar("k", [], "0");bigInt(ctx.getVar("k",[])).lt(bigInt(ctx.getVar("nout",[]))) ? 1 : 0;(ctx.setVar("k", [], bigInt(ctx.getVar("k",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
ctx.setSignal("out", [ctx.getVar("k",[])], bigInt(bigInt(ctx.getVar("k",[])).greater(bigInt(256)) ? 0 : bigInt(ctx.getVar("lin",[])).shr(bigInt(ctx.getVar("k",[]))).and(__MASK__)).and(bigInt("1")).and(__MASK__));
|
||||||
|
ctx.assert(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).mul(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).add(__P__).sub(bigInt("1")).mod(__P__))).mod(__P__), "0");
|
||||||
|
ctx.setVar("lout", [], bigInt(ctx.getVar("lout",[])).add(bigInt(bigInt(ctx.getSignal("out", [ctx.getVar("k",[])])).mul(bigInt(bigInt("2").modPow(bigInt(ctx.getVar("k",[])), __P__))).mod(__P__))).mod(__P__));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.assert(ctx.getVar("lin",[]), ctx.getVar("lout",[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function A(ctx)
|
||||||
|
{
|
||||||
|
ctx.setPin("n2ba", [], "in", [], ctx.getSignal("a", []));
|
||||||
|
ctx.assert(ctx.getPin("n2ba", [], "in", []), ctx.getSignal("a", []));
|
||||||
|
ctx.setPin("n2bb", [], "in", [], ctx.getSignal("b", []));
|
||||||
|
ctx.assert(ctx.getPin("n2bb", [], "in", []), ctx.getSignal("b", []));
|
||||||
|
for (ctx.setVar("i", [], "0");bigInt(ctx.getVar("i",[])).lt(bigInt("32")) ? 1 : 0;(ctx.setVar("i", [], bigInt(ctx.getVar("i",[])).add(bigInt("1")).mod(__P__))).add(__P__).sub(bigInt(1)).mod(__P__))
|
||||||
|
{
|
||||||
|
ctx.setPin("sum", [], "in", ["0",ctx.getVar("i",[])], ctx.getPin("n2ba", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
ctx.assert(ctx.getPin("sum", [], "in", ["0",ctx.getVar("i",[])]), ctx.getPin("n2ba", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
ctx.setPin("sum", [], "in", ["1",ctx.getVar("i",[])], ctx.getPin("n2bb", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
ctx.assert(ctx.getPin("sum", [], "in", ["1",ctx.getVar("i",[])]), ctx.getPin("n2bb", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
ctx.setPin("b2n", [], "in", [ctx.getVar("i",[])], ctx.getPin("sum", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
ctx.assert(ctx.getPin("b2n", [], "in", [ctx.getVar("i",[])]), ctx.getPin("sum", [], "out", [ctx.getVar("i",[])]));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.setSignal("out", [], ctx.getPin("b2n", [], "out", []));
|
||||||
|
ctx.assert(ctx.getSignal("out", []), ctx.getPin("b2n", [], "out", []));
|
||||||
|
}
|
||||||
|
|
||||||
1875
package-lock.json
generated
1875
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "circom",
|
"name": "circom",
|
||||||
"version": "0.5.28",
|
"version": "0.0.7",
|
||||||
"description": "Language to generate logic circuits",
|
"description": "Language to generate logic circuits",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
@@ -29,20 +29,15 @@
|
|||||||
"url": "https://github.com/iden3/circom.git"
|
"url": "https://github.com/iden3/circom.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chai": "^4.2.0",
|
"big-integer": "^1.6.32",
|
||||||
"circom_runtime": "0.1.5",
|
"optimist": "^0.6.1",
|
||||||
"fastfile": "0.0.18",
|
"yargs": "^12.0.2"
|
||||||
"ffiasm": "0.1.1",
|
|
||||||
"ffjavascript": "0.2.22",
|
|
||||||
"ffwasm": "0.0.7",
|
|
||||||
"fnv-plus": "^1.3.1",
|
|
||||||
"r1csfile": "0.0.16",
|
|
||||||
"tmp-promise": "^2.0.2",
|
|
||||||
"wasmbuilder": "0.0.10"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.8.0",
|
"chai": "^4.1.2",
|
||||||
|
"eslint": "^5.0.1",
|
||||||
|
"eslint-plugin-mocha": "^5.0.0",
|
||||||
"jison": "^0.4.18",
|
"jison": "^0.4.18",
|
||||||
"yargs": "^15.3.1"
|
"zksnark": "0.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
149
parser/jaz.jison
149
parser/jaz.jison
@@ -1,22 +1,3 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 0KIMS association.
|
|
||||||
|
|
||||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
|
||||||
|
|
||||||
circom is a free software: you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
||||||
License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* description: Construct AST for jaz language. */
|
/* description: Construct AST for jaz language. */
|
||||||
|
|
||||||
/* lexical grammar */
|
/* lexical grammar */
|
||||||
@@ -40,7 +21,6 @@ if { return 'if'; }
|
|||||||
else { return 'else'; }
|
else { return 'else'; }
|
||||||
for { return 'for'; }
|
for { return 'for'; }
|
||||||
while { return 'while'; }
|
while { return 'while'; }
|
||||||
compute { return 'compute'; }
|
|
||||||
do { return 'do'; }
|
do { return 'do'; }
|
||||||
return { return 'return'; }
|
return { return 'return'; }
|
||||||
include { return 'include'; }
|
include { return 'include'; }
|
||||||
@@ -79,13 +59,11 @@ include { return 'include'; }
|
|||||||
\- { return '-'; }
|
\- { return '-'; }
|
||||||
\* { return '*'; }
|
\* { return '*'; }
|
||||||
\/ { return '/'; }
|
\/ { return '/'; }
|
||||||
\\ { return '\\'; }
|
|
||||||
\% { return '%'; }
|
\% { return '%'; }
|
||||||
\^ { return '^'; }
|
\^ { return '^'; }
|
||||||
\& { return '&'; }
|
\& { return '&'; }
|
||||||
\| { return '|'; }
|
\| { return '|'; }
|
||||||
\! { return '!'; }
|
\! { return '!'; }
|
||||||
\~ { return '~'; }
|
|
||||||
\< { return '<'; }
|
\< { return '<'; }
|
||||||
\> { return '>'; }
|
\> { return '>'; }
|
||||||
\! { return '!'; }
|
\! { return '!'; }
|
||||||
@@ -121,7 +99,7 @@ include { return 'include'; }
|
|||||||
%left '<<' '>>'
|
%left '<<' '>>'
|
||||||
|
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
%left '*' '/' '\\' '%'
|
%left '*' '/' '%'
|
||||||
%left '**'
|
%left '**'
|
||||||
%right '++' '--' UMINUS UPLUS '!' '~'
|
%right '++' '--' UMINUS UPLUS '!' '~'
|
||||||
%left '.'
|
%left '.'
|
||||||
@@ -132,8 +110,10 @@ include { return 'include'; }
|
|||||||
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
const Scalar = require('ffjavascript').Scalar;
|
const bigInt = require('big-integer');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
|
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||||
|
|
||||||
function setLines(dst, first, last) {
|
function setLines(dst, first, last) {
|
||||||
last = last || first;
|
last = last || first;
|
||||||
@@ -198,10 +178,6 @@ statment
|
|||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
}
|
}
|
||||||
| computeStatment
|
|
||||||
{
|
|
||||||
$$ = $1;
|
|
||||||
}
|
|
||||||
| returnStatment
|
| returnStatment
|
||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
@@ -264,12 +240,20 @@ identifierList
|
|||||||
ifStatment
|
ifStatment
|
||||||
: 'if' '(' expression ')' statment 'else' statment
|
: 'if' '(' expression ')' statment 'else' statment
|
||||||
{
|
{
|
||||||
|
if ($3.type == "NUMBER") {
|
||||||
|
$$ = !$3.value.eq(0) ? $5 : $7;
|
||||||
|
} else {
|
||||||
$$ = { type: "IF", condition: $3, then: $5, else: $7 };
|
$$ = { type: "IF", condition: $3, then: $5, else: $7 };
|
||||||
|
}
|
||||||
setLines($$, @1, @7);
|
setLines($$, @1, @7);
|
||||||
}
|
}
|
||||||
| 'if' '(' expression ')' statment
|
| 'if' '(' expression ')' statment
|
||||||
{
|
{
|
||||||
|
if ($3.type == "NUMBER") {
|
||||||
|
$$ = !$3.value.eq(0) ? $5 : { type: "NUMBER", value: bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "IF", condition: $3, then: $5 };
|
$$ = { type: "IF", condition: $3, then: $5 };
|
||||||
|
}
|
||||||
setLines($$, @1, @5);
|
setLines($$, @1, @5);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -298,14 +282,6 @@ doWhileStatment
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
computeStatment
|
|
||||||
: 'compute' statment
|
|
||||||
{
|
|
||||||
$$ = { type: "COMPUTE", body: $2 };
|
|
||||||
setLines($$, @1, @2);
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
returnStatment
|
returnStatment
|
||||||
: 'return' expression ';'
|
: 'return' expression ';'
|
||||||
{
|
{
|
||||||
@@ -441,7 +417,11 @@ e17
|
|||||||
}
|
}
|
||||||
| e17 '?' e17 ':' e17 %prec TERCOND
|
| e17 '?' e17 ':' e17 %prec TERCOND
|
||||||
{
|
{
|
||||||
|
if ($1.type == "NUMBER") {
|
||||||
|
$$ = !$1.value.eq(0) ? $3 : $5;
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "?", values: [$1, $3, $5] };
|
$$ = { type: "OP", op: "?", values: [$1, $3, $5] };
|
||||||
|
}
|
||||||
setLines($$, @1, @5);
|
setLines($$, @1, @5);
|
||||||
}
|
}
|
||||||
| e16 %prec EMPTY
|
| e16 %prec EMPTY
|
||||||
@@ -464,7 +444,11 @@ e16
|
|||||||
e15
|
e15
|
||||||
: e15 '||' e14
|
: e15 '||' e14
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: !$1.value.eq(0) || !$3.value.eq(0) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "||", values: [$1, $3] };
|
$$ = { type: "OP", op: "||", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e14 %prec EMPTY
|
| e14 %prec EMPTY
|
||||||
@@ -476,7 +460,11 @@ e15
|
|||||||
e14
|
e14
|
||||||
: e14 '&&' e13
|
: e14 '&&' e13
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: !$1.value.eq(0) && !$3.value.eq(0) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "&&", values: [$1, $3] };
|
$$ = { type: "OP", op: "&&", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e13 %prec EMPTY
|
| e13 %prec EMPTY
|
||||||
@@ -488,7 +476,11 @@ e14
|
|||||||
e13
|
e13
|
||||||
: e13 '|' e12
|
: e13 '|' e12
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "|", values: [$1, $3] };
|
$$ = { type: "OP", op: "|", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e12 %prec EMPTY
|
| e12 %prec EMPTY
|
||||||
@@ -501,7 +493,11 @@ e13
|
|||||||
e12
|
e12
|
||||||
: e12 '^' e11
|
: e12 '^' e11
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "^", values: [$1, $3] };
|
$$ = { type: "OP", op: "^", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e11 %prec EMPTY
|
| e11 %prec EMPTY
|
||||||
@@ -513,7 +509,11 @@ e12
|
|||||||
e11
|
e11
|
||||||
: e11 '&' e10
|
: e11 '&' e10
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.and($3.value).and(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "&", values: [$1, $3] };
|
$$ = { type: "OP", op: "&", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e10 %prec EMPTY
|
| e10 %prec EMPTY
|
||||||
@@ -528,12 +528,20 @@ e11
|
|||||||
e10
|
e10
|
||||||
: e10 '==' e9
|
: e10 '==' e9
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.equals($3.value) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "==", values: [$1, $3] };
|
$$ = { type: "OP", op: "==", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e10 '!=' e9
|
| e10 '!=' e9
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.eq($3.value) ? bigInt(0) : bigInt(1) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "!=", values: [$1, $3] };
|
$$ = { type: "OP", op: "!=", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e9 %prec EMPTY
|
| e9 %prec EMPTY
|
||||||
@@ -545,22 +553,38 @@ e10
|
|||||||
e9
|
e9
|
||||||
: e9 '<=' e7
|
: e9 '<=' e7
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.lesserOrEquals($3.value) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "<=", values: [$1, $3] };
|
$$ = { type: "OP", op: "<=", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e9 '>=' e7
|
| e9 '>=' e7
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.greaterOrEquals($3.value) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: ">=", values: [$1, $3] };
|
$$ = { type: "OP", op: ">=", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e9 '<' e7
|
| e9 '<' e7
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.lesser($3.value) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "<", values: [$1, $3] };
|
$$ = { type: "OP", op: "<", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e9 '>' e7
|
| e9 '>' e7
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.greater($3.value) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: ">", values: [$1, $3] };
|
$$ = { type: "OP", op: ">", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e7 %prec EMPTY
|
| e7 %prec EMPTY
|
||||||
@@ -572,12 +596,22 @@ e9
|
|||||||
e7
|
e7
|
||||||
: e7 '<<' e6
|
: e7 '<<' e6
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
let v = $3.value.greater(256) ? 256 : $3.value.value;
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.shiftLeft(v).and(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "<<", values: [$1, $3] };
|
$$ = { type: "OP", op: "<<", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e7 '>>' e6
|
| e7 '>>' e6
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
let v = $3.value.greater(256) ? 256 : $3.value.value;
|
||||||
|
$$ = {t1ype: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: ">>", values: [$1, $3] };
|
$$ = { type: "OP", op: ">>", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e6 %prec EMPTY
|
| e6 %prec EMPTY
|
||||||
@@ -589,12 +623,20 @@ e7
|
|||||||
e6
|
e6
|
||||||
: e6 '+' e5
|
: e6 '+' e5
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: ($1.value.plus($3.value)).mod(__P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "+", values: [$1, $3] };
|
$$ = { type: "OP", op: "+", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e6 '-' e5
|
| e6 '-' e5
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: ($1.value.plus(__P__).minus($3.value)).mod(__P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "-", values: [$1, $3] };
|
$$ = { type: "OP", op: "-", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e5 %prec EMPTY
|
| e5 %prec EMPTY
|
||||||
@@ -607,22 +649,29 @@ e6
|
|||||||
e5
|
e5
|
||||||
: e5 '*' e4
|
: e5 '*' e4
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: ($1.value.times($3.value)).mod(__P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "*", values: [$1, $3] };
|
$$ = { type: "OP", op: "*", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e5 '/' e4
|
| e5 '/' e4
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: ($1.value.times($3.value.modInv(__P__))).mod(__P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "/", values: [$1, $3] };
|
$$ = { type: "OP", op: "/", values: [$1, $3] };
|
||||||
setLines($$, @1, @3);
|
|
||||||
}
|
}
|
||||||
| e5 '\\' e4
|
|
||||||
{
|
|
||||||
$$ = { type: "OP", op: "\\", values: [$1, $3] };
|
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e5 '%' e4
|
| e5 '%' e4
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.mod($3.value) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "%", values: [$1, $3] };
|
$$ = { type: "OP", op: "%", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e4 %prec EMPTY
|
| e4 %prec EMPTY
|
||||||
@@ -634,7 +683,11 @@ e5
|
|||||||
e4
|
e4
|
||||||
: e4 '**' e3
|
: e4 '**' e3
|
||||||
{
|
{
|
||||||
|
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||||
|
$$ = { type: "NUMBER", value: $1.value.modPow($3.value, __P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "**", values: [$1, $3] };
|
$$ = { type: "OP", op: "**", values: [$1, $3] };
|
||||||
|
}
|
||||||
setLines($$, @1, @3);
|
setLines($$, @1, @3);
|
||||||
}
|
}
|
||||||
| e3 %prec EMPTY
|
| e3 %prec EMPTY
|
||||||
@@ -662,17 +715,29 @@ e3
|
|||||||
}
|
}
|
||||||
| '-' e3 %prec UMINUS
|
| '-' e3 %prec UMINUS
|
||||||
{
|
{
|
||||||
|
if ($2.type == "NUMBER") {
|
||||||
|
$$ = { type: "NUMBER", value: __P__.minus($2.value).mod(__P__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "UMINUS", values: [$2] };
|
$$ = { type: "OP", op: "UMINUS", values: [$2] };
|
||||||
|
}
|
||||||
setLines($$, @1, @2);
|
setLines($$, @1, @2);
|
||||||
}
|
}
|
||||||
| '!' e3
|
| '!' e3
|
||||||
{
|
{
|
||||||
|
if ($2.type == "NUMBER") {
|
||||||
|
$$ = { type: "NUMBER", value: $2.value.eq(0) ? bigInt(1) : bigInt(0) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "!", values: [$2] };
|
$$ = { type: "OP", op: "!", values: [$2] };
|
||||||
|
}
|
||||||
setLines($$, @1, @2);
|
setLines($$, @1, @2);
|
||||||
}
|
}
|
||||||
| '~' e3
|
| '~' e3
|
||||||
{
|
{
|
||||||
|
if ($2.type == "NUMBER") {
|
||||||
|
$$ = { type: "NUMBER", value: $2.value.xor(__MASK__) };
|
||||||
|
} else {
|
||||||
$$ = { type: "OP", op: "~", values: [$2] };
|
$$ = { type: "OP", op: "~", values: [$2] };
|
||||||
|
}
|
||||||
setLines($$, @1, @2);
|
setLines($$, @1, @2);
|
||||||
}
|
}
|
||||||
| e2 %prec EMPTY
|
| e2 %prec EMPTY
|
||||||
@@ -709,12 +774,12 @@ e0
|
|||||||
}
|
}
|
||||||
| DECNUMBER
|
| DECNUMBER
|
||||||
{
|
{
|
||||||
$$ = {type: "NUMBER", value: Scalar.fromString($1) }
|
$$ = {type: "NUMBER", value: bigInt($1).mod(__P__) }
|
||||||
setLines($$, @1);
|
setLines($$, @1);
|
||||||
}
|
}
|
||||||
| HEXNUMBER
|
| HEXNUMBER
|
||||||
{
|
{
|
||||||
$$ = {type: "NUMBER", value: Scalar.fromString($1.substr(2).toUpperCase(), 16) }
|
$$ = {type: "NUMBER", value: bigInt($1.substr(2).toUpperCase(), 16).mod(__P__) }
|
||||||
setLines($$, @1);
|
setLines($$, @1);
|
||||||
}
|
}
|
||||||
| '(' expression ')' %prec EMPTY
|
| '(' expression ')' %prec EMPTY
|
||||||
|
|||||||
506
parser/jaz.js
506
parser/jaz.js
File diff suppressed because one or more lines are too long
@@ -1,723 +0,0 @@
|
|||||||
const utils = require("../../src/utils");
|
|
||||||
const assert = require("assert");
|
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
|
||||||
const F1Field = require("ffjavascript").F1Field;
|
|
||||||
const BigArray = require("../../src/bigarray");
|
|
||||||
|
|
||||||
function ref2src(c) {
|
|
||||||
if ((c[0] == "R")||(c[0] == "RI")) {
|
|
||||||
return c[1];
|
|
||||||
} else if (c[0] == "V") {
|
|
||||||
return c[1].toString();
|
|
||||||
} else if (c[0] == "C") {
|
|
||||||
return `(ctx->circuit->constants + ${c[1]})`;
|
|
||||||
} else if (c[0] == "CC") {
|
|
||||||
return "__cIdx";
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodeBuilderC {
|
|
||||||
constructor() {
|
|
||||||
this.ops = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
addComment(comment) {
|
|
||||||
this.ops.push({op: "COMMENT", comment});
|
|
||||||
}
|
|
||||||
|
|
||||||
addBlock(block) {
|
|
||||||
this.ops.push({op: "BLOCK", block});
|
|
||||||
}
|
|
||||||
|
|
||||||
calcOffset(dLabel, offsets) {
|
|
||||||
this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(dLabel, src, sOffset) {
|
|
||||||
this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSubComponentOffset(dLabel, component, hash, hashLabel) {
|
|
||||||
this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSubComponentSizes(dLabel, component, hash, hashLabel) {
|
|
||||||
this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSignalOffset(dLabel, component, hash, hashLabel) {
|
|
||||||
this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSignalSizes(dLabel, component, hash, hashLabel) {
|
|
||||||
this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
setSignal(component, signal, value) {
|
|
||||||
this.ops.push({op: "SETSIGNAL", component, signal, value});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSignal(dLabel, component, signal) {
|
|
||||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
|
|
||||||
}
|
|
||||||
|
|
||||||
copyN(dLabel, offset, src, n) {
|
|
||||||
this.ops.push({op: "COPYN", dLabel, offset, src, n});
|
|
||||||
}
|
|
||||||
|
|
||||||
copyNRet(src, n) {
|
|
||||||
this.ops.push({op: "COPYNRET", src, n});
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldOp(dLabel, fOp, params) {
|
|
||||||
this.ops.push({op: "FOP", dLabel, fOp, params});
|
|
||||||
}
|
|
||||||
|
|
||||||
ret() {
|
|
||||||
this.ops.push({op: "RET"});
|
|
||||||
}
|
|
||||||
|
|
||||||
addLoop(condLabel, body) {
|
|
||||||
this.ops.push({op: "LOOP", condLabel, body});
|
|
||||||
}
|
|
||||||
|
|
||||||
addIf(condLabel, thenCode, elseCode) {
|
|
||||||
this.ops.push({op: "IF", condLabel, thenCode, elseCode});
|
|
||||||
}
|
|
||||||
|
|
||||||
fnCall(fnName, retLabel, params) {
|
|
||||||
this.ops.push({op: "FNCALL", fnName, retLabel, params});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkConstraint(a, b, strErr) {
|
|
||||||
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAssert(a, strErr) {
|
|
||||||
this.ops.push({op: "CHECKASSERT", a, strErr});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(val) {
|
|
||||||
this.ops.push({op: "LOG", val});
|
|
||||||
}
|
|
||||||
|
|
||||||
concat(cb) {
|
|
||||||
this.ops.push(...cb.ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasCode() {
|
|
||||||
for (let i=0; i<this.ops.length; i++) {
|
|
||||||
if (this.ops[i].op != "COMMENT") return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildOffset(offsets) {
|
|
||||||
let rN=0;
|
|
||||||
let S = "";
|
|
||||||
offsets.forEach((o) => {
|
|
||||||
if ((o[0][0] == "V") && (o[1][0]== "V")) {
|
|
||||||
rN += o[0][1]*o[1][1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let f="";
|
|
||||||
if (o[0][0] == "V") {
|
|
||||||
if (o[0][1]==0) return;
|
|
||||||
f += o[0][1];
|
|
||||||
} else if (o[0][0] == "RI") {
|
|
||||||
if (o[0][1]==0) return;
|
|
||||||
f += o[0][1];
|
|
||||||
} else if (o[0][0] == "R") {
|
|
||||||
f += `Fr_toInt(${o[0][1]})`;
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
if (o[1][0] == "V") {
|
|
||||||
if (o[1][1]==0) return;
|
|
||||||
if (o[1][1]>1) {
|
|
||||||
f += "*" + o[1][1];
|
|
||||||
}
|
|
||||||
} else if (o[1][0] == "RS") {
|
|
||||||
f += `*${o[1][1]}[${o[1][2]}]`;
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
if (S!="") S+= " + ";
|
|
||||||
S += f;
|
|
||||||
});
|
|
||||||
if (rN>0) {
|
|
||||||
S = `${rN} + ${S}`;
|
|
||||||
}
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
build(code) {
|
|
||||||
this.ops.forEach( (o) => {
|
|
||||||
if (o.op == "COMMENT") {
|
|
||||||
code.push(`/* ${o.comment} */`);
|
|
||||||
} else if (o.op == "BLOCK") {
|
|
||||||
const codeBlock=[];
|
|
||||||
o.block.build(codeBlock);
|
|
||||||
code.push(utils.ident(codeBlock));
|
|
||||||
} else if (o.op == "CALCOFFSETS") {
|
|
||||||
code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`);
|
|
||||||
} else if (o.op == "ASSIGN") {
|
|
||||||
const oS = ref2src(o.sOffset);
|
|
||||||
if (oS != "0") {
|
|
||||||
code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`);
|
|
||||||
} else {
|
|
||||||
code.push(`${o.dLabel} = ${ref2src(o.src)};`);
|
|
||||||
}
|
|
||||||
} else if (o.op == "GETSUBCOMPONENTOFFSET") {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
} else if (o.op == "GETSUBCOMPONENTSIZES") {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
} else if (o.op == "GETSIGNALOFFSET") {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
} else if (o.op == "GETSIGNALSIZES") {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
} else if (o.op == "SETSIGNAL") {
|
|
||||||
code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`);
|
|
||||||
} else if (o.op == "GETSIGNAL") {
|
|
||||||
code.push(`ctx->getSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel});`);
|
|
||||||
} else if (o.op == "COPYN") {
|
|
||||||
const oS = ref2src(o.offset);
|
|
||||||
const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel;
|
|
||||||
code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`);
|
|
||||||
} else if (o.op == "COPYNRET") {
|
|
||||||
code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`);
|
|
||||||
} else if (o.op == "RET") {
|
|
||||||
code.push("goto returnFunc;");
|
|
||||||
} else if (o.op == "FOP") {
|
|
||||||
let paramsS = "";
|
|
||||||
for (let i=0; i<o.params.length; i++) {
|
|
||||||
if (i>0) paramsS += ", ";
|
|
||||||
paramsS += ref2src(o.params[i]);
|
|
||||||
}
|
|
||||||
code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`);
|
|
||||||
} else if (o.op == "LOOP") {
|
|
||||||
code.push(`while (Fr_isTrue(${o.condLabel})) {`);
|
|
||||||
const body = [];
|
|
||||||
o.body.build(body);
|
|
||||||
code.push(utils.ident(body));
|
|
||||||
code.push("}");
|
|
||||||
} else if (o.op == "IF") {
|
|
||||||
code.push(`if (Fr_isTrue(${o.condLabel})) {`);
|
|
||||||
const thenCode = [];
|
|
||||||
o.thenCode.build(thenCode);
|
|
||||||
code.push(utils.ident(thenCode));
|
|
||||||
if (o.elseCode) {
|
|
||||||
code.push("} else {");
|
|
||||||
const elseCode = [];
|
|
||||||
o.elseCode.build(elseCode);
|
|
||||||
code.push(utils.ident(elseCode));
|
|
||||||
}
|
|
||||||
code.push("}");
|
|
||||||
} else if (o.op == "FNCALL") {
|
|
||||||
code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`);
|
|
||||||
} else if (o.op == "CHECKCONSTRAINT") {
|
|
||||||
code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`);
|
|
||||||
} else if (o.op == "CHECKASSERT") {
|
|
||||||
code.push(`ctx->checkAssert(__cIdx, ${ref2src(o.a)}, "${o.strErr}");`);
|
|
||||||
} else if (o.op == "LOG") {
|
|
||||||
code.push(`ctx->log(${ref2src(o.val)});`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FunctionBuilderC {
|
|
||||||
|
|
||||||
constructor(name, instanceDef, type) {
|
|
||||||
this.name = name;
|
|
||||||
this.instanceDef = instanceDef;
|
|
||||||
this.type = type; // "COMPONENT" or "FUNCTIOM"
|
|
||||||
this.definedFrElements = [];
|
|
||||||
this.definedIntElements = [];
|
|
||||||
this.definedSizeElements = [];
|
|
||||||
this.definedPFrElements = [];
|
|
||||||
this.initializedElements = [];
|
|
||||||
this.initializedSignalOffset = [];
|
|
||||||
this.initializedSignalSizes = [];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
defineFrElements(dLabel, size) {
|
|
||||||
this.definedFrElements.push({dLabel, size});
|
|
||||||
}
|
|
||||||
|
|
||||||
defineIntElement(dLabel) {
|
|
||||||
this.definedIntElements.push({dLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
defineSizesElement(dLabel) {
|
|
||||||
this.definedSizeElements.push({dLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
definePFrElement(dLabel) {
|
|
||||||
this.definedPFrElements.push({dLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeFrElement(dLabel, offset, idConstant) {
|
|
||||||
this.initializedElements.push({dLabel, offset, idConstant});
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeSignalOffset(dLabel, component, hash, hashLabel) {
|
|
||||||
this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeSignalSizes(dLabel, component, hash, hashLabel) {
|
|
||||||
this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
|
|
||||||
}
|
|
||||||
|
|
||||||
setParams(params) {
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildHeader(code) {
|
|
||||||
this.definedFrElements.forEach( (o) => {
|
|
||||||
code.push(`FrElement ${o.dLabel}[${o.size}];`);
|
|
||||||
});
|
|
||||||
this.definedIntElements.forEach( (o) => {
|
|
||||||
code.push(`int ${o.dLabel};`);
|
|
||||||
});
|
|
||||||
this.definedSizeElements.forEach( (o) => {
|
|
||||||
code.push(`Circom_Sizes ${o.dLabel};`);
|
|
||||||
});
|
|
||||||
this.definedPFrElements.forEach( (o) => {
|
|
||||||
code.push(`PFrElement ${o.dLabel};`);
|
|
||||||
});
|
|
||||||
this.initializedElements.forEach( (o) => {
|
|
||||||
code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`);
|
|
||||||
});
|
|
||||||
this.initializedSignalOffset.forEach( (o) => {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
});
|
|
||||||
this.initializedSignalSizes.forEach( (o) => {
|
|
||||||
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildFooter(code) {
|
|
||||||
}
|
|
||||||
|
|
||||||
newCodeBuilder() {
|
|
||||||
return new CodeBuilderC();
|
|
||||||
}
|
|
||||||
|
|
||||||
setBody(body) {
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
build(code) {
|
|
||||||
code.push(
|
|
||||||
"/*",
|
|
||||||
this.instanceDef,
|
|
||||||
"*/"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.type=="COMPONENT") {
|
|
||||||
code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`);
|
|
||||||
} else if (this.type=="FUNCTION") {
|
|
||||||
let sParams = "";
|
|
||||||
for (let i=0;i<this.params.length;i++ ) sParams += `, PFrElement ${this.params[i]}`;
|
|
||||||
code.push(`void ${this.name}(Circom_CalcWit *ctx, PFrElement __retValue ${sParams}) {`);
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fnCode = [];
|
|
||||||
this._buildHeader(fnCode);
|
|
||||||
this.body.build(fnCode);
|
|
||||||
if (this.type=="COMPONENT") {
|
|
||||||
fnCode.push("ctx->finished(__cIdx);");
|
|
||||||
} else if (this.type=="FUNCTION") {
|
|
||||||
fnCode.push("returnFunc: ;");
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
this._buildFooter(fnCode);
|
|
||||||
|
|
||||||
code.push(utils.ident(fnCode));
|
|
||||||
code.push("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuilderC {
|
|
||||||
constructor(p, verbose) {
|
|
||||||
this.F = new F1Field(p);
|
|
||||||
|
|
||||||
this.hashMaps={};
|
|
||||||
this.componentEntriesTables=new BigArray();
|
|
||||||
this.sizes ={};
|
|
||||||
this.constants = [];
|
|
||||||
this.functions = [];
|
|
||||||
this.components = new BigArray();
|
|
||||||
this.usedConstants = {};
|
|
||||||
this.verbose = verbose;
|
|
||||||
|
|
||||||
|
|
||||||
this.sizePointers = {};
|
|
||||||
this.hashMapPointers = {};
|
|
||||||
this.functionIdx = {};
|
|
||||||
this.nCets = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeader(header) {
|
|
||||||
this.header=header;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
|
|
||||||
addHashMap(name, hm) {
|
|
||||||
this.hashMaps[name] = hm;
|
|
||||||
}
|
|
||||||
|
|
||||||
addComponentEntriesTable(name, cet, idComponent) {
|
|
||||||
this.componentEntriesTables[idComponent] = {
|
|
||||||
name: name,
|
|
||||||
cet: cet
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
addSizes(name, accSizes) {
|
|
||||||
this.sizes[name] = accSizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
addConstant(c) {
|
|
||||||
c = this.F.e(c);
|
|
||||||
const cS = c.toString();
|
|
||||||
if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
|
|
||||||
this.constants.push(c);
|
|
||||||
this.usedConstants[cS] = this.constants.length - 1;
|
|
||||||
return this.constants.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
addFunction(fnBuilder) {
|
|
||||||
this.functions.push(fnBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
addComponent(component) {
|
|
||||||
this.components.push(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMapIsInput(map) {
|
|
||||||
this.mapIsInput = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWit2Sig(wit2sig) {
|
|
||||||
this.wit2sig = wit2sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
newComponentFunctionBuilder(name, instanceDef) {
|
|
||||||
return new FunctionBuilderC(name, instanceDef, "COMPONENT");
|
|
||||||
}
|
|
||||||
|
|
||||||
newFunctionBuilder(name, instanceDef) {
|
|
||||||
return new FunctionBuilderC(name, instanceDef, "FUNCTION");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Body functions
|
|
||||||
|
|
||||||
_buildHeader(code) {
|
|
||||||
code.push(
|
|
||||||
"#include \"circom.hpp\"",
|
|
||||||
"#include \"calcwit.hpp\"",
|
|
||||||
`#define NSignals ${this.header.NSignals}`,
|
|
||||||
`#define NComponents ${this.header.NComponents}`,
|
|
||||||
`#define NOutputs ${this.header.NOutputs}`,
|
|
||||||
`#define NInputs ${this.header.NInputs}`,
|
|
||||||
`#define NVars ${this.header.NVars}`,
|
|
||||||
`#define __P__ "${this.header.P.toString()}"`,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildHashMaps(fdData) {
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pHashMaps = fdData.pos;
|
|
||||||
|
|
||||||
const buff = new Uint8Array(256*12);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
for (let hmName in this.hashMaps ) {
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.hashMapPointers[hmName] = fdData.pos;
|
|
||||||
const hm = this.hashMaps[hmName];
|
|
||||||
|
|
||||||
for (let i=0; i<256; i++) {
|
|
||||||
buffV.setUint32(i*12, hm[i] ? parseInt( hm[i][0].slice(8), 16 ) : 0, true);
|
|
||||||
buffV.setUint32(i*12+4, hm[i] ? parseInt( hm[i][0].slice(0,8), 16 ) : 0, true);
|
|
||||||
buffV.setUint32(i*12+8, hm[i] ? hm[i][1] : 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildComponentEntriesTables(fdData) {
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pCets = fdData.pos;
|
|
||||||
for (let i=0; i< this.componentEntriesTables.length; i++) {
|
|
||||||
if ((this.verbose)&&(i%100000 ==0)) console.log(`_buildComponentEntriesTables ${i}/${this.componentEntriesTables.length}`);
|
|
||||||
const cet = this.componentEntriesTables[i].cet;
|
|
||||||
|
|
||||||
this.components[i].entryTablePointer = fdData.pos;
|
|
||||||
const buff = new Uint8Array(16*cet.length);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
for (let j=0; j<cet.length; j++) {
|
|
||||||
utils.setUint64(buffV, 16*j+0, this.sizePointers[ cet[j].sizeName]);
|
|
||||||
buffV.setUint32(16*j+8, cet[j].offset, true);
|
|
||||||
buffV.setUint32(16*j+12, cet[j].type == "S" ? 0 : 1, true); // Size type 0-> Signal, 1->Component
|
|
||||||
this.nCets ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildSizes(fdData) {
|
|
||||||
for (let sName in this.sizes) {
|
|
||||||
const accSizes = this.sizes[sName];
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.sizePointers[sName] = fdData.pos;
|
|
||||||
|
|
||||||
const buff = new Uint8Array(4*accSizes.length);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
for (let i=0; i<accSizes.length; i++) {
|
|
||||||
buffV.setUint32(i*4, accSizes[i], true);
|
|
||||||
}
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async _buildConstants(fdData) {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
const frSize = (8 + self.F.n64*8);
|
|
||||||
const buff = new Uint8Array(self.constants.length* frSize);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pConstants = fdData.pos;
|
|
||||||
|
|
||||||
let o = 0;
|
|
||||||
for (let i=0; i<self.constants.length; i++) {
|
|
||||||
Fr2Bytes(buffV, o, self.constants[i]);
|
|
||||||
o += frSize;
|
|
||||||
}
|
|
||||||
await fdData.write(buff);
|
|
||||||
|
|
||||||
|
|
||||||
function Fr2Bytes(buffV, offset, n) {
|
|
||||||
const minShort = self.F.neg(self.F.e("80000000"));
|
|
||||||
const maxShort = self.F.e("7FFFFFFF", 16);
|
|
||||||
|
|
||||||
if ( (self.F.geq(n, minShort))
|
|
||||||
&&(self.F.leq(n, maxShort)))
|
|
||||||
{
|
|
||||||
if (self.F.geq(n, self.F.zero)) {
|
|
||||||
return shortMontgomeryPositive(n);
|
|
||||||
} else {
|
|
||||||
return shortMontgomeryNegative(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return longMontgomery(n);
|
|
||||||
|
|
||||||
|
|
||||||
function shortMontgomeryPositive(a) {
|
|
||||||
buffV.setUint32(offset, Scalar.toNumber(a) , true );
|
|
||||||
buffV.setUint32(offset + 4, 0x40000000 , true );
|
|
||||||
long(buffV, offset + 8, toMontgomery(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function shortMontgomeryNegative(a) {
|
|
||||||
const b = -Scalar.toNumber(self.F.neg(a));
|
|
||||||
buffV.setUint32(offset, b , true );
|
|
||||||
buffV.setUint32(offset + 4, 0x40000000 , true );
|
|
||||||
long(buffV, offset + 8, toMontgomery(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
function longMontgomery(a) {
|
|
||||||
buffV.setUint32(offset, 0 , true );
|
|
||||||
buffV.setUint32(offset + 4, 0xC0000000 , true );
|
|
||||||
long(buffV, offset + 8, toMontgomery(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
function long(buffV, offset, a) {
|
|
||||||
|
|
||||||
let p = offset;
|
|
||||||
const arr = Scalar.toArray(a, 0x100000000);
|
|
||||||
for (let i=0; i<self.F.n64*2; i++) {
|
|
||||||
const idx = arr.length-1-i;
|
|
||||||
|
|
||||||
if ( idx >=0) {
|
|
||||||
buffV.setUint32(p, arr[idx], true);
|
|
||||||
} else {
|
|
||||||
buffV.setUint32(p, 0, true);
|
|
||||||
}
|
|
||||||
p+= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toMontgomery(a) {
|
|
||||||
return self.F.mul(a, self.F.R);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildFunctions(code) {
|
|
||||||
const listedFunctions = [];
|
|
||||||
for (let i=0; i<this.functions.length; i++) {
|
|
||||||
const cfb = this.functions[i];
|
|
||||||
cfb.build(code);
|
|
||||||
if (this.functions[i].type == "COMPONENT") {
|
|
||||||
this.functionIdx[this.functions[i].name] = listedFunctions.length;
|
|
||||||
listedFunctions.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
code.push("// Function Table");
|
|
||||||
code.push(`Circom_ComponentFunction _functionTable[${listedFunctions.length}] = {`);
|
|
||||||
for (let i=0; i<listedFunctions.length; i++) {
|
|
||||||
const sep = i>0 ? " ," : " ";
|
|
||||||
code.push(`${sep}${this.functions[listedFunctions[i]].name}`);
|
|
||||||
}
|
|
||||||
code.push("};");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async _buildComponents(fdData) {
|
|
||||||
|
|
||||||
const buff = new Uint8Array(32);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pComponents = fdData.pos;
|
|
||||||
|
|
||||||
for (let i=0; i<this.components.length; i++) {
|
|
||||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildComponents ${i}/${this.components.length}`);
|
|
||||||
const c = this.components[i];
|
|
||||||
|
|
||||||
utils.setUint64(buffV, 0, this.hashMapPointers[c.hashMapName], true);
|
|
||||||
utils.setUint64(buffV, 8, c.entryTablePointer, true);
|
|
||||||
utils.setUint64(buffV, 16, this.functionIdx[c.functionName], true);
|
|
||||||
buffV.setUint32(24, c.nInSignals, true);
|
|
||||||
buffV.setUint32(28, c.newThread ? 1 : 0, true);
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildMapIsInput(fdData) {
|
|
||||||
|
|
||||||
const buff = new Uint8Array(this.mapIsInput.length * 4);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pMapIsInput = fdData.pos;
|
|
||||||
|
|
||||||
for (let i=0; i<this.mapIsInput.length; i++) {
|
|
||||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildMapIsInput ${i}/${this.mapIsInput.length}`);
|
|
||||||
|
|
||||||
buffV.setUint32(4*i, this.mapIsInput[i], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildWit2Sig(fdData) {
|
|
||||||
|
|
||||||
const buff = new Uint8Array(this.wit2sig.length * 4);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
this.pWit2Sig = fdData.pos;
|
|
||||||
|
|
||||||
for (let i=0; i<this.wit2sig.length; i++) {
|
|
||||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildWit2Sig ${i}/${this.wit2sig.length}`);
|
|
||||||
|
|
||||||
buffV.setUint32(4*i, this.wit2sig[i], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _buildCircuitVar(fdData) {
|
|
||||||
|
|
||||||
const pP = fdData.pos;
|
|
||||||
|
|
||||||
const strBuff = new TextEncoder("utf-8").encode(this.header.P.toString());
|
|
||||||
await fdData.write(strBuff);
|
|
||||||
|
|
||||||
const buff = new Uint8Array(72);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
|
|
||||||
utils.setUint64(buffV, 0, this.pWit2Sig, true);
|
|
||||||
utils.setUint64(buffV, 8, this.pComponents, true);
|
|
||||||
utils.setUint64(buffV, 16, this.pMapIsInput, true);
|
|
||||||
utils.setUint64(buffV, 24, this.pConstants, true);
|
|
||||||
utils.setUint64(buffV, 32, pP, true);
|
|
||||||
utils.setUint64(buffV, 40, this.pCets, true);
|
|
||||||
|
|
||||||
buffV.setUint32(48, this.header.NSignals, true);
|
|
||||||
buffV.setUint32(52, this.header.NComponents, true);
|
|
||||||
buffV.setUint32(56, this.header.NOutputs, true);
|
|
||||||
buffV.setUint32(60, this.header.NInputs, true);
|
|
||||||
buffV.setUint32(64, this.header.NVars, true);
|
|
||||||
buffV.setUint32(68, this.nCets, true);
|
|
||||||
|
|
||||||
fdData.pos = 0;
|
|
||||||
|
|
||||||
await fdData.write(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async build(fdCode, fdData) {
|
|
||||||
const encoder = new TextEncoder("utf-8");
|
|
||||||
fdData.pos = 72;
|
|
||||||
while (fdData.pos % 8) fdData.pos++;
|
|
||||||
|
|
||||||
const code=new BigArray();
|
|
||||||
this._buildHeader(code);
|
|
||||||
await this._buildSizes(fdData);
|
|
||||||
await this._buildConstants(fdData);
|
|
||||||
await this._buildHashMaps(fdData);
|
|
||||||
await this._buildComponentEntriesTables(fdData);
|
|
||||||
this._buildFunctions(code);
|
|
||||||
await this._buildComponents(fdData);
|
|
||||||
await this._buildMapIsInput(fdData);
|
|
||||||
await this._buildWit2Sig(fdData);
|
|
||||||
await this._buildCircuitVar(fdData);
|
|
||||||
await writeCode(code);
|
|
||||||
|
|
||||||
async function writeCode(c) {
|
|
||||||
if (c.push) {
|
|
||||||
for (let i=0; i<c.length; i++) {
|
|
||||||
await writeCode(c[i]);
|
|
||||||
}
|
|
||||||
} else if (typeof c === "string") {
|
|
||||||
await fdCode.write(encoder.encode(c + "\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = BuilderC;
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
const chai = require("chai");
|
|
||||||
const assert = chai.assert;
|
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
var tmp = require("tmp-promise");
|
|
||||||
const path = require("path");
|
|
||||||
const compiler = require("../../src/compiler");
|
|
||||||
const util = require("util");
|
|
||||||
const exec = util.promisify(require("child_process").exec);
|
|
||||||
|
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
|
||||||
const utils = require("../../src/utils");
|
|
||||||
const loadR1cs = require("r1csfile").load;
|
|
||||||
const ZqField = require("ffjavascript").ZqField;
|
|
||||||
const buildZqField = require("ffiasm").buildZqField;
|
|
||||||
const fastFile = require("fastfile");
|
|
||||||
|
|
||||||
const {stringifyBigInts, unstringifyBigInts } = require("ffjavascript").utils;
|
|
||||||
|
|
||||||
module.exports = c_tester;
|
|
||||||
|
|
||||||
|
|
||||||
async function c_tester(circomFile, _options) {
|
|
||||||
tmp.setGracefulCleanup();
|
|
||||||
|
|
||||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
|
||||||
|
|
||||||
// console.log(dir.path);
|
|
||||||
|
|
||||||
const baseName = path.basename(circomFile, ".circom");
|
|
||||||
const options = Object.assign({}, _options);
|
|
||||||
|
|
||||||
options.cSourceFile = await fastFile.createOverride(path.join(dir.path, baseName + ".cpp"));
|
|
||||||
options.dataFile = await fastFile.createOverride(path.join(dir.path, baseName + ".dat"));
|
|
||||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
|
||||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
|
||||||
|
|
||||||
options.p = options.p || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
||||||
await compiler(circomFile, options);
|
|
||||||
|
|
||||||
await options.cSourceFile.close();
|
|
||||||
await options.dataFile.close();
|
|
||||||
|
|
||||||
const source = await buildZqField(options.p, "Fr");
|
|
||||||
|
|
||||||
// console.log(dir.path);
|
|
||||||
|
|
||||||
await fs.promises.writeFile(path.join(dir.path, "fr.asm"), source.asm, "utf8");
|
|
||||||
await fs.promises.writeFile(path.join(dir.path, "fr.hpp"), source.hpp, "utf8");
|
|
||||||
await fs.promises.writeFile(path.join(dir.path, "fr.cpp"), source.cpp, "utf8");
|
|
||||||
|
|
||||||
let pThread = "";
|
|
||||||
|
|
||||||
if (process.platform === "darwin") {
|
|
||||||
await exec("nasm -fmacho64 --prefix _ " +
|
|
||||||
` ${path.join(dir.path, "fr.asm")}`
|
|
||||||
);
|
|
||||||
} else if (process.platform === "linux") {
|
|
||||||
pThread = "-pthread";
|
|
||||||
await exec("nasm -felf64 " +
|
|
||||||
` ${path.join(dir.path, "fr.asm")}`
|
|
||||||
);
|
|
||||||
} else throw("Unsupported platform");
|
|
||||||
|
|
||||||
const cdir = path.join(path.dirname(require.resolve("circom_runtime")), "c");
|
|
||||||
|
|
||||||
await exec("g++" + ` ${pThread}` +
|
|
||||||
` ${path.join(cdir, "main.cpp")}` +
|
|
||||||
` ${path.join(cdir, "calcwit.cpp")}` +
|
|
||||||
` ${path.join(cdir, "utils.cpp")}` +
|
|
||||||
` ${path.join(dir.path, "fr.cpp")}` +
|
|
||||||
` ${path.join(dir.path, "fr.o")}` +
|
|
||||||
` ${path.join(dir.path, baseName + ".cpp")} ` +
|
|
||||||
` -o ${path.join(dir.path, baseName)}` +
|
|
||||||
` -I ${dir.path} -I${cdir}` +
|
|
||||||
" -lgmp -std=c++11 -DSANITY_CHECK -g"
|
|
||||||
);
|
|
||||||
|
|
||||||
// console.log(dir.path);
|
|
||||||
return new CTester(dir, baseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CTester {
|
|
||||||
|
|
||||||
constructor(dir, baseName) {
|
|
||||||
this.dir=dir;
|
|
||||||
this.baseName = baseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
async release() {
|
|
||||||
await this.dir.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
async calculateWitness(input) {
|
|
||||||
await fs.promises.writeFile(
|
|
||||||
path.join(this.dir.path, "in.json"),
|
|
||||||
JSON.stringify(stringifyBigInts(input), null, 1)
|
|
||||||
);
|
|
||||||
const r = await exec(`${path.join(this.dir.path, this.baseName)}` +
|
|
||||||
` ${path.join(this.dir.path, "in.json")}` +
|
|
||||||
` ${path.join(this.dir.path, "out.json")}`
|
|
||||||
);
|
|
||||||
if (r.stdout) {
|
|
||||||
console.log(r.stdout);
|
|
||||||
}
|
|
||||||
const resStr = await fs.promises.readFile(
|
|
||||||
path.join(this.dir.path, "out.json")
|
|
||||||
);
|
|
||||||
|
|
||||||
const res = unstringifyBigInts(JSON.parse(resStr));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadSymbols() {
|
|
||||||
if (this.symbols) return;
|
|
||||||
this.symbols = {};
|
|
||||||
const symsStr = await fs.promises.readFile(
|
|
||||||
path.join(this.dir.path, this.baseName + ".sym"),
|
|
||||||
"utf8"
|
|
||||||
);
|
|
||||||
const lines = symsStr.split("\n");
|
|
||||||
for (let i=0; i<lines.length; i++) {
|
|
||||||
const arr = lines[i].split(",");
|
|
||||||
if (arr.length!=4) continue;
|
|
||||||
this.symbols[arr[3]] = {
|
|
||||||
labelIdx: Number(arr[0]),
|
|
||||||
varIdx: Number(arr[1]),
|
|
||||||
componentIdx: Number(arr[2]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadConstraints() {
|
|
||||||
const self = this;
|
|
||||||
if (this.constraints) return;
|
|
||||||
const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
|
|
||||||
self.F = new ZqField(r1cs.prime);
|
|
||||||
self.nVars = r1cs.nVars;
|
|
||||||
self.constraints = r1cs.constraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertOut(actualOut, expectedOut) {
|
|
||||||
const self = this;
|
|
||||||
if (!self.symbols) await self.loadSymbols();
|
|
||||||
|
|
||||||
checkObject("main", expectedOut);
|
|
||||||
|
|
||||||
function checkObject(prefix, eOut) {
|
|
||||||
|
|
||||||
if (Array.isArray(eOut)) {
|
|
||||||
for (let i=0; i<eOut.length; i++) {
|
|
||||||
checkObject(prefix + "["+i+"]", eOut[i]);
|
|
||||||
}
|
|
||||||
} else if ((typeof eOut == "object")&&(eOut.constructor.name == "Object")) {
|
|
||||||
for (let k in eOut) {
|
|
||||||
checkObject(prefix + "."+k, eOut[k]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof self.symbols[prefix] == "undefined") {
|
|
||||||
assert(false, "Output variable not defined: "+ prefix);
|
|
||||||
}
|
|
||||||
const ba = actualOut[self.symbols[prefix].varIdx].toString();
|
|
||||||
const be = eOut.toString();
|
|
||||||
assert.strictEqual(ba, be, prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDecoratedOutput(witness) {
|
|
||||||
const self = this;
|
|
||||||
const lines = [];
|
|
||||||
if (!self.symbols) await self.loadSymbols();
|
|
||||||
for (let n in self.symbols) {
|
|
||||||
let v;
|
|
||||||
if (utils.isDefined(witness[self.symbols[n].varIdx])) {
|
|
||||||
v = witness[self.symbols[n].varIdx].toString();
|
|
||||||
} else {
|
|
||||||
v = "undefined";
|
|
||||||
}
|
|
||||||
lines.push(`${n} --> ${v}`);
|
|
||||||
}
|
|
||||||
return lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkConstraints(witness) {
|
|
||||||
const self = this;
|
|
||||||
if (!self.constraints) await self.loadConstraints();
|
|
||||||
for (let i=0; i<self.constraints.length; i++) {
|
|
||||||
checkConstraint(self.constraints[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkConstraint(constraint) {
|
|
||||||
const F = self.F;
|
|
||||||
const a = evalLC(constraint[0]);
|
|
||||||
const b = evalLC(constraint[1]);
|
|
||||||
const c = evalLC(constraint[2]);
|
|
||||||
|
|
||||||
assert (F.sub(F.mul(a,b), c).isZero(), "Constraint doesn't match");
|
|
||||||
}
|
|
||||||
|
|
||||||
function evalLC(lc) {
|
|
||||||
const F = self.F;
|
|
||||||
let v = F.zero;
|
|
||||||
for (let w in lc) {
|
|
||||||
v = F.add(
|
|
||||||
v,
|
|
||||||
F.mul( lc[w], witness[w] )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,875 +0,0 @@
|
|||||||
const errs = require("./errs");
|
|
||||||
|
|
||||||
|
|
||||||
const buildWasmFf = require("ffwasm").buildWasmFf;
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function buildRuntime(module, builder) {
|
|
||||||
|
|
||||||
const pSanityCheck = module.alloc(4);
|
|
||||||
|
|
||||||
function buildInit() {
|
|
||||||
const f = module.addFunction("init");
|
|
||||||
f.addParam("sanityCheck", "i32");
|
|
||||||
f.addLocal("i", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
// Set the stack to current memory
|
|
||||||
f.addCode(
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_const(4),
|
|
||||||
c.i32_shl(
|
|
||||||
c.i32_and(
|
|
||||||
c.current_memory(),
|
|
||||||
c.i32_const(0xFFFFFFF8)
|
|
||||||
),
|
|
||||||
c.i32_const(16)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Save Sanity check flag
|
|
||||||
f.addCode(
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_const(pSanityCheck),
|
|
||||||
c.getLocal("sanityCheck")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
// i=0
|
|
||||||
c.setLocal("i", c.i32_const(0)),
|
|
||||||
c.block(c.loop(
|
|
||||||
// if (i==NComponents) break
|
|
||||||
c.br_if(1, c.i32_eq(c.getLocal("i"), c.i32_const(builder.header.NComponents))),
|
|
||||||
|
|
||||||
// inputSignalsToTrigger[i] = components[i].nInputSignals
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pInputSignalsToTrigger),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_load(c.i32_const(builder.ppComponents)),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(builder.sizeofComponent) // Sizeof component
|
|
||||||
)
|
|
||||||
),
|
|
||||||
builder.offsetComponentNInputSignals
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
// i=i+1
|
|
||||||
c.setLocal(
|
|
||||||
"i",
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br(0)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
// i=0
|
|
||||||
c.setLocal("i", c.i32_const(0)),
|
|
||||||
c.block(c.loop(
|
|
||||||
// if (i==NSignals) break
|
|
||||||
c.br_if(1, c.i32_eq(c.getLocal("i"), c.i32_const(builder.header.NSignals))),
|
|
||||||
|
|
||||||
// signalsAssigned[i] = false
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_const(0)
|
|
||||||
),
|
|
||||||
|
|
||||||
// i=i+1
|
|
||||||
c.setLocal(
|
|
||||||
"i",
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br(0)
|
|
||||||
))
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.call(
|
|
||||||
"Fr_copy",
|
|
||||||
c.i32_const(builder.pSignals),
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_load(c.i32_const(builder.ppConstants)),
|
|
||||||
c.i32_const(builder.addConstant(1) * builder.sizeFr)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
// i=0
|
|
||||||
c.setLocal("i", c.i32_const(0)),
|
|
||||||
c.block(c.loop(
|
|
||||||
// if (i==NComponents) break
|
|
||||||
c.br_if(1, c.i32_eq(c.getLocal("i"), c.i32_const(builder.header.NComponents))),
|
|
||||||
|
|
||||||
// if (inputSignalsToTrigger[i] == 0) triggerComponent(i)
|
|
||||||
c.if(
|
|
||||||
c.i32_eqz(
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pInputSignalsToTrigger),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"triggerComponent",
|
|
||||||
c.getLocal("i")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
// i=i+1
|
|
||||||
c.setLocal(
|
|
||||||
"i",
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br(0)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifSanityCheck(c, ...args) {
|
|
||||||
return c.if(
|
|
||||||
c.i32_load(c.i32_const(pSanityCheck)),
|
|
||||||
[].concat(...[...args])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildTriggerComponent() {
|
|
||||||
const f = module.addFunction("triggerComponent");
|
|
||||||
f.addParam("component", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.call_indirect(
|
|
||||||
c.getLocal("component"), // Idx in table
|
|
||||||
c.getLocal("component") // Parameter
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildHash2ComponentEntry() {
|
|
||||||
const f = module.addFunction("hash2ComponentEntry");
|
|
||||||
f.addParam("component", "i32");
|
|
||||||
f.addParam("hash", "i64");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
|
|
||||||
f.addLocal("pComponent", "i32");
|
|
||||||
f.addLocal("pHashTable", "i32");
|
|
||||||
f.addLocal("hIdx", "i32");
|
|
||||||
f.addLocal("h", "i64");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.setLocal(
|
|
||||||
"pComponent",
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_load(c.i32_const(builder.ppComponents)), // pComponents
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.i32_const(20) // sizeof(Component)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.setLocal(
|
|
||||||
"pHashTable",
|
|
||||||
c.i32_load(c.getLocal("pComponent"))
|
|
||||||
),
|
|
||||||
c.setLocal(
|
|
||||||
"hIdx",
|
|
||||||
c.i32_and(
|
|
||||||
c.i32_wrap_i64(c.getLocal("hash")),
|
|
||||||
c.i32_const(0xFF)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.block(c.loop(
|
|
||||||
c.setLocal(
|
|
||||||
"h",
|
|
||||||
c.i64_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("pHashTable"),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("hIdx"),
|
|
||||||
c.i32_const(12)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br_if(1, c.i64_eq(c.getLocal("h"), c.getLocal("hash"))),
|
|
||||||
c.if(
|
|
||||||
c.i64_eqz(c.getLocal("h")),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.HASH_NOT_FOUND.code),
|
|
||||||
c.i32_const(errs.HASH_NOT_FOUND.pointer),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.setLocal(
|
|
||||||
"hIdx",
|
|
||||||
c.i32_and(
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("hIdx"),
|
|
||||||
c.i32_const(1)
|
|
||||||
),
|
|
||||||
c.i32_const(0xFF)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br(0)
|
|
||||||
)),
|
|
||||||
|
|
||||||
c.i32_add( // pComponentEntry
|
|
||||||
c.i32_load( // pComponentEntryTable
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("pComponent"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_mul(
|
|
||||||
c.i32_load( // idx to the componentEntry
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("pHashTable"),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("hIdx"),
|
|
||||||
c.i32_const(12)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
8
|
|
||||||
),
|
|
||||||
c.i32_const(12)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetFromComponentEntry(fnName, offset, type) {
|
|
||||||
const f = module.addFunction(fnName);
|
|
||||||
f.addParam("pR", "i32");
|
|
||||||
f.addParam("component", "i32");
|
|
||||||
f.addParam("hash", "i64");
|
|
||||||
f.addLocal("pComponentEntry", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.setLocal(
|
|
||||||
"pComponentEntry",
|
|
||||||
c.call(
|
|
||||||
"hash2ComponentEntry",
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.getLocal("hash")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.if( // If type is not signal
|
|
||||||
c.i32_ne(
|
|
||||||
c.i32_load(
|
|
||||||
c.getLocal("pComponentEntry"),
|
|
||||||
8 // type offset
|
|
||||||
),
|
|
||||||
c.i32_const(type)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.INVALID_TYPE.code),
|
|
||||||
c.i32_const(errs.INVALID_TYPE.pointer),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_store(
|
|
||||||
c.getLocal("pR"),
|
|
||||||
c.i32_load(
|
|
||||||
c.getLocal("pComponentEntry"),
|
|
||||||
offset
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const f2 = module.addFunction(fnName + "32");
|
|
||||||
f2.addParam("pR", "i32");
|
|
||||||
f2.addParam("component", "i32");
|
|
||||||
f2.addParam("hashMSB", "i32");
|
|
||||||
f2.addParam("hashLSB", "i32");
|
|
||||||
|
|
||||||
const c2 = f2.getCodeBuilder();
|
|
||||||
|
|
||||||
f2.addCode(
|
|
||||||
c2.call(
|
|
||||||
fnName,
|
|
||||||
c2.getLocal("pR"),
|
|
||||||
c2.getLocal("component"),
|
|
||||||
c2.i64_or(
|
|
||||||
c2.i64_shl(
|
|
||||||
c2.i64_extend_i32_u(c2.getLocal("hashMSB")),
|
|
||||||
c2.i64_const(32)
|
|
||||||
),
|
|
||||||
c2.i64_extend_i32_u(c2.getLocal("hashLSB"))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildGetSignal() {
|
|
||||||
const f = module.addFunction("getSignal");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
f.addParam("pR", "i32");
|
|
||||||
f.addParam("component", "i32");
|
|
||||||
f.addParam("signal", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.if(
|
|
||||||
c.i32_eqz(
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.ACCESSING_NOT_ASSIGNED_SIGNAL.code),
|
|
||||||
c.i32_const(errs.ACCESSING_NOT_ASSIGNED_SIGNAL.pointer),
|
|
||||||
c.getLocal("cIdx"),
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.call(
|
|
||||||
"Fr_copy",
|
|
||||||
c.getLocal("pR"),
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignals),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(builder.sizeFr)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.call("logGetSignal", c.getLocal("signal"), c.getLocal("pR") )
|
|
||||||
));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildSetSignal() {
|
|
||||||
const f = module.addFunction("setSignal");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
f.addParam("component", "i32");
|
|
||||||
f.addParam("signal", "i32");
|
|
||||||
f.addParam("pVal", "i32");
|
|
||||||
f.addLocal("signalsToTrigger", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.call("logSetSignal", c.getLocal("signal"), c.getLocal("pVal") ),
|
|
||||||
c.if(
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.SIGNAL_ASSIGNED_TWICE.code),
|
|
||||||
c.i32_const(errs.SIGNAL_ASSIGNED_TWICE.pointer),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_store(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_const(1)
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.call(
|
|
||||||
"Fr_copy",
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignals),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(builder.sizeFr)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.getLocal("pVal"),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.if( // If ( mapIsInput[s >> 5] & 1 << (s & 0x1f) )
|
|
||||||
c.i32_and(
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_load(c.i32_const(builder.ppMapIsInput)),
|
|
||||||
c.i32_shl(
|
|
||||||
c.i32_shr_u(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(5)
|
|
||||||
),
|
|
||||||
c.i32_const(2)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.i32_shl(
|
|
||||||
c.i32_const(1),
|
|
||||||
c.i32_and(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(0x1F)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
[
|
|
||||||
|
|
||||||
...c.setLocal(
|
|
||||||
"signalsToTrigger",
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pInputSignalsToTrigger),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
...c.if( // if (signalsToTrigger > 0)
|
|
||||||
c.i32_gt_u(
|
|
||||||
c.getLocal("signalsToTrigger"),
|
|
||||||
c.i32_const(0)
|
|
||||||
),
|
|
||||||
[
|
|
||||||
...c.setLocal( // signalsToTrigger--
|
|
||||||
"signalsToTrigger",
|
|
||||||
c.i32_sub(
|
|
||||||
c.getLocal("signalsToTrigger"),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
...c.i32_store(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pInputSignalsToTrigger),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.getLocal("signalsToTrigger"),
|
|
||||||
),
|
|
||||||
...c.if( // if (signalsToTrigger==0) triggerCompomnent(component)
|
|
||||||
c.i32_eqz(c.getLocal("signalsToTrigger")),
|
|
||||||
c.call(
|
|
||||||
"triggerComponent",
|
|
||||||
c.getLocal("component")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.MAPISINPUT_DONT_MATCH.code),
|
|
||||||
c.i32_const(errs.MAPISINPUT_DONT_MATCH.pointer),
|
|
||||||
c.getLocal("component"),
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComponentFinished() {
|
|
||||||
const f = module.addFunction("componentFinished");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.call("logFinishComponent", c.getLocal("cIdx"))
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(c.ret([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComponentStarted() {
|
|
||||||
const f = module.addFunction("componentStarted");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.call("logStartComponent", c.getLocal("cIdx"))
|
|
||||||
));
|
|
||||||
|
|
||||||
f.addCode(c.ret([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCheckConstraint() {
|
|
||||||
const pTmp = module.alloc(builder.sizeFr);
|
|
||||||
const f = module.addFunction("checkConstraint");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
f.addParam("pA", "i32");
|
|
||||||
f.addParam("pB", "i32");
|
|
||||||
f.addParam("pStr", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.call(
|
|
||||||
"Fr_eq",
|
|
||||||
c.i32_const(pTmp),
|
|
||||||
c.getLocal("pA"),
|
|
||||||
c.getLocal("pB")
|
|
||||||
),
|
|
||||||
c.if (
|
|
||||||
c.i32_eqz(
|
|
||||||
c.call(
|
|
||||||
"Fr_isTrue",
|
|
||||||
c.i32_const(pTmp),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.CONSTRAIN_DOES_NOT_MATCH.code),
|
|
||||||
c.i32_const(errs.CONSTRAIN_DOES_NOT_MATCH.pointer),
|
|
||||||
c.getLocal("cIdx"),
|
|
||||||
c.getLocal("pA"),
|
|
||||||
c.getLocal("pB"),
|
|
||||||
c.getLocal("pStr"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCheckAssert() {
|
|
||||||
const f = module.addFunction("checkAssert");
|
|
||||||
f.addParam("cIdx", "i32");
|
|
||||||
f.addParam("pA", "i32");
|
|
||||||
f.addParam("pStr", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(ifSanityCheck(c,
|
|
||||||
c.if (
|
|
||||||
c.i32_eqz(
|
|
||||||
c.call(
|
|
||||||
"Fr_isTrue",
|
|
||||||
c.getLocal("pA"),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.ASSERT_DOES_NOT_MATCH.code),
|
|
||||||
c.i32_const(errs.ASSERT_DOES_NOT_MATCH.pointer),
|
|
||||||
c.getLocal("cIdx"),
|
|
||||||
c.getLocal("pA"),
|
|
||||||
c.getLocal("pStr"),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetNVars() {
|
|
||||||
const f = module.addFunction("getNVars");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(c.i32_const(builder.header.NVars));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetFrLen() {
|
|
||||||
const f = module.addFunction("getFrLen");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.i32_const(builder.sizeFr));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetPRawPrime() {
|
|
||||||
const f = module.addFunction("getPRawPrime");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.i32_const(module.modules["Fr_F1m"].pq));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetPWitness() {
|
|
||||||
const f = module.addFunction("getPWitness");
|
|
||||||
f.addParam("w", "i32");
|
|
||||||
f.addLocal("signal", "i32");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.setLocal(
|
|
||||||
"signal",
|
|
||||||
c.i32_load( // wit2sig[w]
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_load( c.i32_const(builder.ppWit2sig)),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("w"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (builder.sanityCheck) {
|
|
||||||
f.addCode(
|
|
||||||
c.if(
|
|
||||||
c.i32_eqz(
|
|
||||||
c.i32_load(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignalsAssigned),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(4)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.call(
|
|
||||||
"error",
|
|
||||||
c.i32_const(errs.ACCESSING_NOT_ASSIGNED_SIGNAL.code),
|
|
||||||
c.i32_const(errs.ACCESSING_NOT_ASSIGNED_SIGNAL.pointer),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0),
|
|
||||||
c.i32_const(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pSignals),
|
|
||||||
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("signal"),
|
|
||||||
c.i32_const(builder.sizeFr)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetWitnessBuffer() {
|
|
||||||
const f = module.addFunction("getWitnessBuffer");
|
|
||||||
f.setReturnType("i32");
|
|
||||||
f.addLocal("i", "i32");
|
|
||||||
f.addLocal("pSrc", "i32");
|
|
||||||
f.addLocal("pDst", "i32");
|
|
||||||
|
|
||||||
const c = f.getCodeBuilder();
|
|
||||||
|
|
||||||
f.addCode(
|
|
||||||
c.setLocal("i", c.i32_const(0)),
|
|
||||||
c.block(c.loop(
|
|
||||||
// if (i==NComponents) break
|
|
||||||
c.br_if(1, c.i32_eq(c.getLocal("i"), c.i32_const(builder.header.NVars))),
|
|
||||||
|
|
||||||
c.setLocal(
|
|
||||||
"pSrc",
|
|
||||||
c.call(
|
|
||||||
"getPWitness",
|
|
||||||
c.getLocal("i"),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
c.call(
|
|
||||||
"Fr_toLongNormal",
|
|
||||||
c.getLocal("pSrc")
|
|
||||||
),
|
|
||||||
|
|
||||||
c.setLocal(
|
|
||||||
"pDst",
|
|
||||||
c.i32_add(
|
|
||||||
c.i32_const(builder.pOutput),
|
|
||||||
c.i32_mul(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(builder.sizeFr-8)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
c.call(
|
|
||||||
"Fr_F1m_copy",
|
|
||||||
c.i32_add(c.getLocal("pSrc"), c.i32_const(8)),
|
|
||||||
c.getLocal("pDst")
|
|
||||||
),
|
|
||||||
|
|
||||||
// i=i+1
|
|
||||||
c.setLocal(
|
|
||||||
"i",
|
|
||||||
c.i32_add(
|
|
||||||
c.getLocal("i"),
|
|
||||||
c.i32_const(1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
c.br(0)
|
|
||||||
)),
|
|
||||||
|
|
||||||
c.i32_const(builder.pOutput)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const fError = module.addIimportFunction("error", "runtime");
|
|
||||||
fError.addParam("code", "i32");
|
|
||||||
fError.addParam("pStr", "i32");
|
|
||||||
fError.addParam("param1", "i32");
|
|
||||||
fError.addParam("param2", "i32");
|
|
||||||
fError.addParam("param3", "i32");
|
|
||||||
fError.addParam("param4", "i32");
|
|
||||||
|
|
||||||
const fLogSetSignal = module.addIimportFunction("logSetSignal", "runtime");
|
|
||||||
fLogSetSignal.addParam("signal", "i32");
|
|
||||||
fLogSetSignal.addParam("val", "i32");
|
|
||||||
|
|
||||||
const fLogGetSignal = module.addIimportFunction("logGetSignal", "runtime");
|
|
||||||
fLogGetSignal.addParam("signal", "i32");
|
|
||||||
fLogGetSignal.addParam("val", "i32");
|
|
||||||
|
|
||||||
const fLogFinishComponent = module.addIimportFunction("logFinishComponent", "runtime");
|
|
||||||
fLogFinishComponent.addParam("cIdx", "i32");
|
|
||||||
|
|
||||||
const fLogStartComponent = module.addIimportFunction("logStartComponent", "runtime");
|
|
||||||
fLogStartComponent.addParam("cIdx", "i32");
|
|
||||||
|
|
||||||
const fLog = module.addIimportFunction("log", "runtime");
|
|
||||||
fLog.addParam("code", "i32");
|
|
||||||
|
|
||||||
buildWasmFf(module, "Fr", builder.header.P);
|
|
||||||
|
|
||||||
builder.pSignals=module.alloc(builder.header.NSignals*builder.sizeFr);
|
|
||||||
builder.pOutput=module.alloc(builder.header.NVars*(builder.sizeFr-8));
|
|
||||||
builder.pInputSignalsToTrigger=module.alloc(builder.header.NComponents*4);
|
|
||||||
builder.pSignalsAssigned=module.alloc(builder.header.NSignals*4);
|
|
||||||
|
|
||||||
buildHash2ComponentEntry();
|
|
||||||
|
|
||||||
buildTriggerComponent();
|
|
||||||
buildInit();
|
|
||||||
|
|
||||||
buildGetFromComponentEntry("getSubComponentOffset", 0 /* offset */, builder.TYPE_COMPONENT);
|
|
||||||
buildGetFromComponentEntry("getSubComponentSizes", 4 /* offset */, builder.TYPE_COMPONENT);
|
|
||||||
|
|
||||||
buildGetFromComponentEntry("getSignalOffset", 0 /* offset */, builder.TYPE_SIGNAL);
|
|
||||||
buildGetFromComponentEntry("getSignalSizes", 4 /* offset */, builder.TYPE_SIGNAL);
|
|
||||||
|
|
||||||
buildGetSignal();
|
|
||||||
buildSetSignal();
|
|
||||||
|
|
||||||
buildComponentStarted();
|
|
||||||
buildComponentFinished();
|
|
||||||
|
|
||||||
buildCheckConstraint();
|
|
||||||
buildCheckAssert();
|
|
||||||
|
|
||||||
buildGetNVars();
|
|
||||||
buildGetFrLen();
|
|
||||||
buildGetPWitness();
|
|
||||||
buildGetPRawPrime();
|
|
||||||
buildGetWitnessBuffer();
|
|
||||||
|
|
||||||
// buildFrToInt();
|
|
||||||
|
|
||||||
module.exportFunction("init");
|
|
||||||
module.exportFunction("getNVars");
|
|
||||||
module.exportFunction("getFrLen");
|
|
||||||
module.exportFunction("getSignalOffset32");
|
|
||||||
module.exportFunction("setSignal");
|
|
||||||
module.exportFunction("getPWitness");
|
|
||||||
module.exportFunction("Fr_toInt");
|
|
||||||
module.exportFunction("getPRawPrime");
|
|
||||||
module.exportFunction("getWitnessBuffer");
|
|
||||||
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
STACK_OUT_OF_MEM: {code: 1, str: "Stack out of memory"},
|
|
||||||
STACK_TOO_SMALL: {code: 2, str: "Stack too small"},
|
|
||||||
HASH_NOT_FOUND: {code: 3, str: "Hash not found"},
|
|
||||||
INVALID_TYPE: {code: 4, str: "Invalid type"},
|
|
||||||
ACCESSING_NOT_ASSIGNED_SIGNAL: {code: 5, str: "Accessing a not assigned signal"},
|
|
||||||
SIGNAL_ASSIGNED_TWICE: {code: 6, str: "Signal assigned twice"},
|
|
||||||
CONSTRAIN_DOES_NOT_MATCH: {code: 7, str: "Constraint doesn't match"},
|
|
||||||
MAPISINPUT_DONT_MATCH: {code: 8, str: "MapIsInput don't match"},
|
|
||||||
ASSERT_DOES_NOT_MATCH: {code: 9, str: "Assert not satisfied"},
|
|
||||||
};
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
const chai = require("chai");
|
|
||||||
const assert = chai.assert;
|
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
var tmp = require("tmp-promise");
|
|
||||||
const path = require("path");
|
|
||||||
const compiler = require("../../src/compiler");
|
|
||||||
|
|
||||||
const utils = require("../../src/utils");
|
|
||||||
const loadR1cs = require("r1csfile").load;
|
|
||||||
const ZqField = require("ffjavascript").ZqField;
|
|
||||||
const fastFile = require("fastfile");
|
|
||||||
|
|
||||||
const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
|
|
||||||
|
|
||||||
module.exports = wasm_tester;
|
|
||||||
|
|
||||||
async function wasm_tester(circomFile, _options) {
|
|
||||||
tmp.setGracefulCleanup();
|
|
||||||
|
|
||||||
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
|
|
||||||
|
|
||||||
// console.log(dir.path);
|
|
||||||
|
|
||||||
const baseName = path.basename(circomFile, ".circom");
|
|
||||||
const options = Object.assign({}, _options);
|
|
||||||
|
|
||||||
options.wasmFile = await fastFile.createOverride(path.join(dir.path, baseName + ".wasm"));
|
|
||||||
|
|
||||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
|
||||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
|
||||||
|
|
||||||
await compiler(circomFile, options);
|
|
||||||
|
|
||||||
await options.wasmFile.close();
|
|
||||||
|
|
||||||
const wasm = await fs.promises.readFile(path.join(dir.path, baseName + ".wasm"));
|
|
||||||
|
|
||||||
const wc = await WitnessCalculatorBuilder(wasm);
|
|
||||||
|
|
||||||
return new WasmTester(dir, baseName, wc);
|
|
||||||
}
|
|
||||||
|
|
||||||
class WasmTester {
|
|
||||||
|
|
||||||
constructor(dir, baseName, witnessCalculator) {
|
|
||||||
this.dir=dir;
|
|
||||||
this.baseName = baseName;
|
|
||||||
this.witnessCalculator = witnessCalculator;
|
|
||||||
}
|
|
||||||
|
|
||||||
async release() {
|
|
||||||
await this.dir.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
async calculateWitness(input, sanityCheck) {
|
|
||||||
return await this.witnessCalculator.calculateWitness(input, sanityCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadSymbols() {
|
|
||||||
if (this.symbols) return;
|
|
||||||
this.symbols = {};
|
|
||||||
const symsStr = await fs.promises.readFile(
|
|
||||||
path.join(this.dir.path, this.baseName + ".sym"),
|
|
||||||
"utf8"
|
|
||||||
);
|
|
||||||
const lines = symsStr.split("\n");
|
|
||||||
for (let i=0; i<lines.length; i++) {
|
|
||||||
const arr = lines[i].split(",");
|
|
||||||
if (arr.length!=4) continue;
|
|
||||||
this.symbols[arr[3]] = {
|
|
||||||
labelIdx: Number(arr[0]),
|
|
||||||
varIdx: Number(arr[1]),
|
|
||||||
componentIdx: Number(arr[2]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadConstraints() {
|
|
||||||
const self = this;
|
|
||||||
if (this.constraints) return;
|
|
||||||
const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
|
|
||||||
self.F = new ZqField(r1cs.prime);
|
|
||||||
self.nVars = r1cs.nVars;
|
|
||||||
self.constraints = r1cs.constraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertOut(actualOut, expectedOut) {
|
|
||||||
const self = this;
|
|
||||||
if (!self.symbols) await self.loadSymbols();
|
|
||||||
|
|
||||||
checkObject("main", expectedOut);
|
|
||||||
|
|
||||||
function checkObject(prefix, eOut) {
|
|
||||||
|
|
||||||
if (Array.isArray(eOut)) {
|
|
||||||
for (let i=0; i<eOut.length; i++) {
|
|
||||||
checkObject(prefix + "["+i+"]", eOut[i]);
|
|
||||||
}
|
|
||||||
} else if ((typeof eOut == "object")&&(eOut.constructor.name == "Object")) {
|
|
||||||
for (let k in eOut) {
|
|
||||||
checkObject(prefix + "."+k, eOut[k]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof self.symbols[prefix] == "undefined") {
|
|
||||||
assert(false, "Output variable not defined: "+ prefix);
|
|
||||||
}
|
|
||||||
const ba = actualOut[self.symbols[prefix].varIdx].toString();
|
|
||||||
const be = eOut.toString();
|
|
||||||
assert.strictEqual(ba, be, prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDecoratedOutput(witness) {
|
|
||||||
const self = this;
|
|
||||||
const lines = [];
|
|
||||||
if (!self.symbols) await self.loadSymbols();
|
|
||||||
for (let n in self.symbols) {
|
|
||||||
let v;
|
|
||||||
if (utils.isDefined(witness[self.symbols[n].varIdx])) {
|
|
||||||
v = witness[self.symbols[n].varIdx].toString();
|
|
||||||
} else {
|
|
||||||
v = "undefined";
|
|
||||||
}
|
|
||||||
lines.push(`${n} --> ${v}`);
|
|
||||||
}
|
|
||||||
return lines.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkConstraints(witness) {
|
|
||||||
const self = this;
|
|
||||||
if (!self.constraints) await self.loadConstraints();
|
|
||||||
for (let i=0; i<self.constraints.length; i++) {
|
|
||||||
checkConstraint(self.constraints[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkConstraint(constraint) {
|
|
||||||
const F = self.F;
|
|
||||||
const a = evalLC(constraint[0]);
|
|
||||||
const b = evalLC(constraint[1]);
|
|
||||||
const c = evalLC(constraint[2]);
|
|
||||||
|
|
||||||
assert (F.isZero(F.sub(F.mul(a,b), c)), "Constraint doesn't match");
|
|
||||||
}
|
|
||||||
|
|
||||||
function evalLC(lc) {
|
|
||||||
const F = self.F;
|
|
||||||
let v = F.zero;
|
|
||||||
for (let w in lc) {
|
|
||||||
v = F.add(
|
|
||||||
v,
|
|
||||||
F.mul( lc[w], witness[w] )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
const SUBARRAY_SIZE = 0x40000;
|
|
||||||
|
|
||||||
const BigArrayHandler = {
|
|
||||||
get: function(obj, prop) {
|
|
||||||
if (!isNaN(prop)) {
|
|
||||||
return obj.getElement(prop);
|
|
||||||
} else return obj[prop];
|
|
||||||
},
|
|
||||||
set: function(obj, prop, value) {
|
|
||||||
if (!isNaN(prop)) {
|
|
||||||
return obj.setElement(prop, value);
|
|
||||||
} else {
|
|
||||||
obj[prop] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class _BigArray {
|
|
||||||
constructor (initSize) {
|
|
||||||
this.length = initSize || 0;
|
|
||||||
this.arr = new Array(SUBARRAY_SIZE);
|
|
||||||
|
|
||||||
for (let i=0; i<initSize; i+=SUBARRAY_SIZE) {
|
|
||||||
this.arr[i/SUBARRAY_SIZE] = new Array(Math.min(SUBARRAY_SIZE, initSize - i));
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
push () {
|
|
||||||
for (let i=0; i<arguments.length; i++) {
|
|
||||||
this.setElement (this.length, arguments[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getElement(idx) {
|
|
||||||
idx = parseInt(idx);
|
|
||||||
const idx1 = Math.floor(idx / SUBARRAY_SIZE);
|
|
||||||
const idx2 = idx % SUBARRAY_SIZE;
|
|
||||||
return this.arr[idx1] ? this.arr[idx1][idx2] : undefined;
|
|
||||||
}
|
|
||||||
setElement(idx, value) {
|
|
||||||
idx = parseInt(idx);
|
|
||||||
const idx1 = Math.floor(idx / SUBARRAY_SIZE);
|
|
||||||
if (!this.arr[idx1]) {
|
|
||||||
this.arr[idx1] = new Array(SUBARRAY_SIZE);
|
|
||||||
}
|
|
||||||
const idx2 = idx % SUBARRAY_SIZE;
|
|
||||||
this.arr[idx1][idx2] = value;
|
|
||||||
if (idx >= this.length) this.length = idx+1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
getKeys() {
|
|
||||||
const newA = new BigArray();
|
|
||||||
for (let i=0; i<this.arr.length; i++) {
|
|
||||||
if (this.arr[i]) {
|
|
||||||
for (let j=0; j<this.arr[i].length; j++) {
|
|
||||||
if (typeof this.arr[i][j] !== "undefined") {
|
|
||||||
newA.push(i*SUBARRAY_SIZE+j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BigArray {
|
|
||||||
constructor( initSize ) {
|
|
||||||
const obj = new _BigArray(initSize);
|
|
||||||
const extObj = new Proxy(obj, BigArrayHandler);
|
|
||||||
return extObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = BigArray;
|
|
||||||
451
src/build.js
451
src/build.js
@@ -1,451 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 0KIMS association.
|
|
||||||
|
|
||||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
|
||||||
|
|
||||||
circom is a free software: you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
||||||
License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const assert = require("assert");
|
|
||||||
const utils = require("./utils");
|
|
||||||
const gen = require("./gencode").gen;
|
|
||||||
const createRefs = require("./gencode").createRefs;
|
|
||||||
const BigArray = require("./bigarray");
|
|
||||||
|
|
||||||
module.exports = build;
|
|
||||||
|
|
||||||
|
|
||||||
function build(ctx) {
|
|
||||||
ctx.definedFunctions = {};
|
|
||||||
ctx.functionCodes = [];
|
|
||||||
ctx.buildFunction = buildFunction;
|
|
||||||
ctx.conditionalCodeHeader = "";
|
|
||||||
ctx.codes_sizes = [];
|
|
||||||
ctx.definedSizes = {};
|
|
||||||
ctx.addSizes = addSizes;
|
|
||||||
ctx.addConstant = addConstant;
|
|
||||||
ctx.addConstant(ctx.F.zero);
|
|
||||||
ctx.addConstant(ctx.F.one);
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("buildHeader...");
|
|
||||||
buildHeader(ctx);
|
|
||||||
if (ctx.verbose) console.log("buildEntryTables...");
|
|
||||||
buildEntryTables(ctx);
|
|
||||||
ctx.globalNames = ctx.uniqueNames;
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("buildCode...");
|
|
||||||
buildCode(ctx);
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("buildComponentsArray...");
|
|
||||||
buildComponentsArray(ctx);
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("buildMapIsInput...");
|
|
||||||
buildMapIsInput(ctx);
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("buildWit2Sig...");
|
|
||||||
buildWit2Sig(ctx);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildEntryTables(ctx) {
|
|
||||||
|
|
||||||
const definedHashMaps = {};
|
|
||||||
for (let i=0; i<ctx.components.length; i++) {
|
|
||||||
if (ctx.verbose && (i%100000 ==0)) console.log(`buildEntryTables component: ${i}/${ctx.components.length}`);
|
|
||||||
const {htName, htMap} = addHashTable(i);
|
|
||||||
|
|
||||||
const componentEntriesTableName = ctx.getUniqueName("_entryTable" + ctx.components[i].template);
|
|
||||||
|
|
||||||
const componentEntriesTable = [];
|
|
||||||
for (let j=0; j<htMap.length; j++) {
|
|
||||||
const entry = ctx.components[i].names.o[htMap[j]];
|
|
||||||
const sizeName = ctx.addSizes(entry.sizes);
|
|
||||||
componentEntriesTable.push({
|
|
||||||
offset: entry.offset,
|
|
||||||
sizeName: sizeName,
|
|
||||||
type: entry.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.builder.addComponentEntriesTable(componentEntriesTableName, componentEntriesTable, i);
|
|
||||||
|
|
||||||
ctx.components[i].htName = htName;
|
|
||||||
ctx.components[i].etName = componentEntriesTableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
function addHashTable(cIdx) {
|
|
||||||
const keys = Object.keys(ctx.components[cIdx].names.o);
|
|
||||||
assert(keys.length<128);
|
|
||||||
keys.sort((a,b) => ((a>b) ? 1 : -1));
|
|
||||||
const h = utils.fnvHash(keys.join(","));
|
|
||||||
if (definedHashMaps[h]) return definedHashMaps[h];
|
|
||||||
definedHashMaps[h] = {};
|
|
||||||
definedHashMaps[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template);
|
|
||||||
definedHashMaps[h].htMap = [];
|
|
||||||
const t = [];
|
|
||||||
for (let i=0; i<keys.length; i++) {
|
|
||||||
definedHashMaps[h].htMap[i] = keys[i];
|
|
||||||
|
|
||||||
const h2 = utils.fnvHash(keys[i]);
|
|
||||||
let pos = parseInt(h2.slice(-2), 16);
|
|
||||||
while (t[pos]) pos = (pos + 1) % 256;
|
|
||||||
t[pos] = [h2, i, keys[i]];
|
|
||||||
}
|
|
||||||
ctx.builder.addHashMap(definedHashMaps[h].htName, t);
|
|
||||||
|
|
||||||
return definedHashMaps[h];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCode(ctx) {
|
|
||||||
|
|
||||||
const fDefined = {};
|
|
||||||
|
|
||||||
const fnComponents = [];
|
|
||||||
for (let i=0; i<ctx.components.length; i++) {
|
|
||||||
if (ctx.verbose && (i%100000 ==0)) console.log(`buildCode component: ${i}/${ctx.components.length}`);
|
|
||||||
const {h, instanceDef} = hashComponentCall(ctx, i);
|
|
||||||
const fName = ctx.components[i].template+"_"+h;
|
|
||||||
if (!fDefined[fName]) {
|
|
||||||
|
|
||||||
|
|
||||||
ctx.scopes = [{}];
|
|
||||||
ctx.conditionalCode = false;
|
|
||||||
ctx.fnBuilder = ctx.builder.newComponentFunctionBuilder(fName, instanceDef);
|
|
||||||
ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
|
|
||||||
ctx.uniqueNames = Object.assign({},ctx.globalNames);
|
|
||||||
ctx.refs = [];
|
|
||||||
ctx.fileName = ctx.templates[ctx.components[i].template].fileName;
|
|
||||||
ctx.filePath = ctx.templates[ctx.components[i].template].filePath;
|
|
||||||
ctx.getSignalSizesCache = {};
|
|
||||||
ctx.getSignalOffsetCache = {};
|
|
||||||
|
|
||||||
for (let p in ctx.components[i].params) {
|
|
||||||
if (ctx.scopes[0][p]) return ctx.throwError(`Repeated parameter at ${ctx.components[i].template}: ${p}`);
|
|
||||||
const refId = ctx.refs.length;
|
|
||||||
ctx.refs.push({
|
|
||||||
type: "BIGINT",
|
|
||||||
used: false,
|
|
||||||
value: utils.flatArray(ctx.components[i].params[p]),
|
|
||||||
sizes: utils.accSizes(utils.extractSizes(ctx.components[i].params[p])),
|
|
||||||
label: ctx.getUniqueName(p)
|
|
||||||
});
|
|
||||||
ctx.scopes[0][p] = refId;
|
|
||||||
}
|
|
||||||
|
|
||||||
createRefs(ctx, ctx.templates[ctx.components[i].template].block);
|
|
||||||
if (ctx.error) return;
|
|
||||||
|
|
||||||
gen(ctx, ctx.templates[ctx.components[i].template].block);
|
|
||||||
if (ctx.error) return;
|
|
||||||
|
|
||||||
ctx.fnBuilder.setBody(ctx.codeBuilder);
|
|
||||||
|
|
||||||
ctx.builder.addFunction(ctx.fnBuilder);
|
|
||||||
|
|
||||||
fDefined[fName] = true;
|
|
||||||
}
|
|
||||||
ctx.components[i].fnName = fName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fnComponents;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComponentsArray(ctx) {
|
|
||||||
for (let i=0; i< ctx.components.length; i++) {
|
|
||||||
if (ctx.verbose && (i%1000000 ==0)) console.log(`buildComponentsArray component: ${i}/${ctx.components.length}`);
|
|
||||||
let newThread;
|
|
||||||
if (ctx.newThreadTemplates) {
|
|
||||||
if (ctx.newThreadTemplates.test(ctx.components[i].template)) {
|
|
||||||
newThread = true;
|
|
||||||
} else {
|
|
||||||
newThread = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newThread = false;
|
|
||||||
}
|
|
||||||
ctx.builder.addComponent({
|
|
||||||
hashMapName: ctx.components[i].htName,
|
|
||||||
entryTableName: ctx.components[i].etName,
|
|
||||||
functionName: ctx.components[i].fnName,
|
|
||||||
nInSignals: ctx.components[i].nInSignals,
|
|
||||||
newThread: newThread
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function buildHeader(ctx) {
|
|
||||||
ctx.builder.setHeader({
|
|
||||||
NSignals: ctx.signals.length,
|
|
||||||
NComponents: ctx.components.length,
|
|
||||||
NInputs: ctx.components[ ctx.getComponentIdx("main") ].nInSignals,
|
|
||||||
NOutputs: ctx.totals[ ctx.stOUTPUT ],
|
|
||||||
NVars: ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL],
|
|
||||||
P: ctx.F.p
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMapIsInput(ctx) {
|
|
||||||
let i;
|
|
||||||
let map = [];
|
|
||||||
let acc = 0;
|
|
||||||
for (i=0; i<ctx.signals.length; i++) {
|
|
||||||
if (ctx.verbose && (i%1000000 ==0)) console.log(`buildMapIsInput signal: ${i}/${ctx.signals.length}`);
|
|
||||||
if (ctx.signals[i].o & ctx.IN) {
|
|
||||||
acc = acc | (1 << (i%32) );
|
|
||||||
}
|
|
||||||
if ((i+1)%32==0) {
|
|
||||||
map.push(acc);
|
|
||||||
acc = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((i%32) != 0) {
|
|
||||||
map.push(acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.builder.setMapIsInput(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function buildWit2Sig(ctx) {
|
|
||||||
const NVars =
|
|
||||||
ctx.totals[ctx.stONE] +
|
|
||||||
ctx.totals[ctx.stOUTPUT] +
|
|
||||||
ctx.totals[ctx.stPUBINPUT] +
|
|
||||||
ctx.totals[ctx.stPRVINPUT] +
|
|
||||||
ctx.totals[ctx.stINTERNAL];
|
|
||||||
const arr = new BigArray(NVars);
|
|
||||||
for (let i=0; i<ctx.signals.length; i++) {
|
|
||||||
if (ctx.verbose && (i%1000000 ==0)) console.log(`buildWit2Sig signal: ${i}/${ctx.signals.length}`);
|
|
||||||
const outIdx = ctx.signals[i].id;
|
|
||||||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
|
||||||
assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
|
|
||||||
if (outIdx>=NVars) continue; // Is a constant or a discarded variable
|
|
||||||
if (typeof arr[ctx.signals[i].id] == "undefined") {
|
|
||||||
arr[outIdx] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.builder.setWit2Sig(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function addSizes(_sizes) {
|
|
||||||
const sizes = _sizes || [];
|
|
||||||
let name = "sizes";
|
|
||||||
for (let i=0; i<sizes.length;i++) {
|
|
||||||
name+="_"+sizes[i];
|
|
||||||
}
|
|
||||||
if (name=="sizes") name="sizes_0";
|
|
||||||
|
|
||||||
if (this.definedSizes[name]) return this.definedSizes[name];
|
|
||||||
const labelName = this.getUniqueName(name);
|
|
||||||
this.definedSizes[name] = labelName;
|
|
||||||
|
|
||||||
const accSizes = utils.accSizes(sizes);
|
|
||||||
|
|
||||||
this.builder.addSizes(labelName, accSizes);
|
|
||||||
|
|
||||||
let code = `Circom_Size ${labelName}[${accSizes.length}] = {`;
|
|
||||||
for (let i=0; i<accSizes.length; i++) {
|
|
||||||
if (i>0) code += ",";
|
|
||||||
code += accSizes[i];
|
|
||||||
}
|
|
||||||
code += "};\n";
|
|
||||||
this.codes_sizes.push(code);
|
|
||||||
|
|
||||||
return labelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addConstant(c) {
|
|
||||||
return this.builder.addConstant(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildFunction(name, paramValues) {
|
|
||||||
const ctx = this;
|
|
||||||
const {h, instanceDef} = hashFunctionCall(ctx, name, paramValues);
|
|
||||||
|
|
||||||
if (ctx.definedFunctions[h]) return ctx.definedFunctions[h];
|
|
||||||
|
|
||||||
const res = {
|
|
||||||
fnName: `${name}_${h}`
|
|
||||||
};
|
|
||||||
|
|
||||||
const oldRefs = ctx.refs;
|
|
||||||
const oldConditionalCode = ctx.conditionalCode;
|
|
||||||
const oldCodeBuilder = ctx.codeBuilder;
|
|
||||||
const oldFnBuilder = ctx.fnBuilder;
|
|
||||||
|
|
||||||
const oldUniqueNames = ctx.uniqueNames;
|
|
||||||
const oldFileName = ctx.fileName;
|
|
||||||
const oldFilePath = ctx.oldFilePath;
|
|
||||||
const oldReturnSizes = ctx.returnSizes;
|
|
||||||
const oldReturnValue = ctx.returnValue;
|
|
||||||
|
|
||||||
|
|
||||||
ctx.scopes = [{}];
|
|
||||||
ctx.refs = [];
|
|
||||||
ctx.conditionalCode = false;
|
|
||||||
ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef, ctx.functions[name].params);
|
|
||||||
ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
|
|
||||||
ctx.uniqueNames = Object.assign({},ctx.globalNames);
|
|
||||||
ctx.returnValue = null;
|
|
||||||
ctx.returnSizes = null;
|
|
||||||
ctx.fileName = ctx.functions[name].fileName;
|
|
||||||
ctx.filePath = ctx.functions[name].filePath;
|
|
||||||
|
|
||||||
let paramLabels = [];
|
|
||||||
|
|
||||||
for (let i=0; i<ctx.functions[name].params.length; i++) {
|
|
||||||
|
|
||||||
if (paramValues[i].used) {
|
|
||||||
paramLabels.push(ctx.functions[name].params[i]);
|
|
||||||
const idRef = ctx.refs.length;
|
|
||||||
ctx.refs.push({
|
|
||||||
type: "BIGINT",
|
|
||||||
used: true,
|
|
||||||
sizes: paramValues[i].sizes,
|
|
||||||
label: ctx.functions[name].params[i],
|
|
||||||
});
|
|
||||||
ctx.scopes[0][ctx.functions[name].params[i]] = idRef;
|
|
||||||
} else {
|
|
||||||
const idRef = ctx.refs.length;
|
|
||||||
ctx.refs.push({
|
|
||||||
type: "BIGINT",
|
|
||||||
used: false,
|
|
||||||
sizes: paramValues[i].sizes,
|
|
||||||
label: ctx.functions[name].params[i],
|
|
||||||
value: paramValues[i].value
|
|
||||||
});
|
|
||||||
ctx.scopes[0][ctx.functions[name].params[i]] = idRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fnBuilder.setParams(paramLabels);
|
|
||||||
|
|
||||||
createRefs(ctx, ctx.functions[name].block);
|
|
||||||
if (ctx.error) return;
|
|
||||||
|
|
||||||
gen(ctx, ctx.functions[name].block);
|
|
||||||
if (ctx.error) return;
|
|
||||||
|
|
||||||
if (ctx.returnValue == null) {
|
|
||||||
if (ctx.returnSizes == null) assert(false, `Funciont ${name} does not return any value`);
|
|
||||||
|
|
||||||
ctx.fnBuilder.setBody(ctx.codeBuilder);
|
|
||||||
ctx.builder.addFunction(ctx.fnBuilder);
|
|
||||||
|
|
||||||
res.type = "VARVAL_CONSTSIZE";
|
|
||||||
res.returnSizes = ctx.returnSizes;
|
|
||||||
} else {
|
|
||||||
res.type = "CONSTVAL";
|
|
||||||
res.returnValue = ctx.returnValue;
|
|
||||||
res.returnSizes = ctx.returnSizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.refs = oldRefs;
|
|
||||||
ctx.conditionalCode = oldConditionalCode;
|
|
||||||
ctx.codeBuilder = oldCodeBuilder;
|
|
||||||
ctx.fnBuilder = oldFnBuilder;
|
|
||||||
ctx.uniqueNames = oldUniqueNames;
|
|
||||||
ctx.fileName = oldFileName;
|
|
||||||
ctx.filePath = oldFilePath;
|
|
||||||
ctx.returnSizes = oldReturnSizes;
|
|
||||||
ctx.returnValue = oldReturnValue;
|
|
||||||
|
|
||||||
ctx.definedFunctions[h] = res;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hashComponentCall(ctx, cIdx) {
|
|
||||||
// TODO: At the moment generate a diferent function for each instance of the component
|
|
||||||
const constParams = [];
|
|
||||||
for (let p in ctx.components[cIdx].params) {
|
|
||||||
constParams.push(p + "=" + value2str(ctx.F, ctx.components[cIdx].params[p]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let n in ctx.components[cIdx].names.o) {
|
|
||||||
const entry = ctx.components[cIdx].names.o[n];
|
|
||||||
if ((entry.type == "S")&&(ctx.signals[entry.offset].o & ctx.IN)) {
|
|
||||||
travelSizes(n, entry.offset, entry.sizes, (prefix, offset) => {
|
|
||||||
if (utils.isDefined(ctx.signals[offset].v)) {
|
|
||||||
constParams.push(prefix + "=" + ctx.F.e(ctx.signals[offset].v));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let instanceDef = ctx.components[cIdx].template;
|
|
||||||
if (constParams.length>0) {
|
|
||||||
instanceDef += "\n";
|
|
||||||
constParams.sort();
|
|
||||||
instanceDef += constParams.join("\n");
|
|
||||||
}
|
|
||||||
const h = utils.fnvHash(instanceDef);
|
|
||||||
return {h, instanceDef};
|
|
||||||
|
|
||||||
function travelSizes(prefix, offset, sizes, fn) {
|
|
||||||
if (sizes.length == 0) {
|
|
||||||
fn(prefix, offset);
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
let o = offset;
|
|
||||||
for (let i=0; i<sizes[0]; i++) {
|
|
||||||
o += travelSizes(prefix + "[" + i + "]", o, sizes.slice(1), fn);
|
|
||||||
}
|
|
||||||
return o-offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashFunctionCall(ctx, name, paramValues) {
|
|
||||||
// TODO
|
|
||||||
const constParams = [];
|
|
||||||
for (let i=0; i<ctx.functions[name].params.length; i++) {
|
|
||||||
if (!paramValues[i].used) {
|
|
||||||
constParams.push(ctx.functions[name].params[i] + utils.accSizes2Str(paramValues[i].sizes) + "=" + value2str(ctx.F, paramValues[i].value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let instanceDef = name;
|
|
||||||
if (constParams.length>0) {
|
|
||||||
instanceDef += "\n";
|
|
||||||
constParams.sort();
|
|
||||||
instanceDef += constParams.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
const h = utils.fnvHash(instanceDef);
|
|
||||||
return {h, instanceDef};
|
|
||||||
}
|
|
||||||
|
|
||||||
function value2str(F, v) {
|
|
||||||
if (Array.isArray(v)) {
|
|
||||||
let S="[";
|
|
||||||
for (let i=0; i<v.length; i++) {
|
|
||||||
if (i>0) S+=",";
|
|
||||||
S+=value2str(F, v[i]);
|
|
||||||
}
|
|
||||||
S+="]";
|
|
||||||
return S;
|
|
||||||
} else {
|
|
||||||
return F.toString(F.e(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
158
src/buildsyms.js
158
src/buildsyms.js
@@ -1,158 +0,0 @@
|
|||||||
const Readable = require("stream").Readable;
|
|
||||||
|
|
||||||
module.exports = function buildSyms(ctx) {
|
|
||||||
const rs = Readable();
|
|
||||||
|
|
||||||
let it = new ComponentIt(ctx, 0, "main");
|
|
||||||
|
|
||||||
let counter = 0;
|
|
||||||
|
|
||||||
rs._read = function() {
|
|
||||||
const actual = it.current();
|
|
||||||
if (actual == null ) {
|
|
||||||
rs.push(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let s=actual.offset;
|
|
||||||
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
|
|
||||||
let wId = ctx.signals[s].id;
|
|
||||||
if (typeof(wId) == "undefined") wId=-1;
|
|
||||||
rs.push(`${actual.offset},${wId},${actual.cIdx},${actual.name}\n`);
|
|
||||||
|
|
||||||
it.next();
|
|
||||||
counter ++;
|
|
||||||
if ((ctx.verbose)&&(counter%10000 == 0)) console.log("Symbols saved: "+counter);
|
|
||||||
};
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SignalIt {
|
|
||||||
constructor (ctx, offset, prefix, cIdx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.offset = offset;
|
|
||||||
this.prefix = prefix;
|
|
||||||
this.cur = 0;
|
|
||||||
this.cIdx = cIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
next() {
|
|
||||||
this.cur = 1;
|
|
||||||
|
|
||||||
return this.current();
|
|
||||||
}
|
|
||||||
|
|
||||||
current() {
|
|
||||||
if (this.cur == 0) {
|
|
||||||
return {offset: this.offset, name: this.prefix, cIdx: this.cIdx};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArrayIt {
|
|
||||||
constructor (ctx, type, sizes, offset, prefix, cIdx) {
|
|
||||||
if (sizes.length == 0) {
|
|
||||||
if (type == "S") {
|
|
||||||
return new SignalIt(ctx, offset, prefix, cIdx);
|
|
||||||
} else {
|
|
||||||
return new ComponentIt(ctx, offset, prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.type = type;
|
|
||||||
this.sizes = sizes;
|
|
||||||
this.offset = offset;
|
|
||||||
this.prefix = prefix;
|
|
||||||
this.cIdx = cIdx;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.subIt = null;
|
|
||||||
this.cur = 0;
|
|
||||||
|
|
||||||
this.subArrSize = 1;
|
|
||||||
|
|
||||||
for (let i=1; i<sizes.length; i++) {
|
|
||||||
this.subArrSize *= sizes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loadSubIt();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadSubIt() {
|
|
||||||
if (this.cur < this.sizes[0]) {
|
|
||||||
this.subIt = new ArrayIt(this.ctx, this.type, this.sizes.slice(1), this.offset + this.cur*this.subArrSize, this.prefix + "[" + this.cur + "]", this.cIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next() {
|
|
||||||
if (this.subIt) {
|
|
||||||
const res = this.subIt.next();
|
|
||||||
if (res == null) {
|
|
||||||
this.subIt = null;
|
|
||||||
this.cur++;
|
|
||||||
this._loadSubIt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.current();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
current() {
|
|
||||||
if (this.subIt) {
|
|
||||||
return this.subIt.current();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComponentIt {
|
|
||||||
constructor (ctx, idxComponent, prefix) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.idxComponent = idxComponent;
|
|
||||||
this.prefix = prefix;
|
|
||||||
this.names = Object.keys(ctx.components[idxComponent].names.o);
|
|
||||||
|
|
||||||
this.subIt = null;
|
|
||||||
this.cur = 0;
|
|
||||||
this._loadSubIt();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadSubIt() {
|
|
||||||
if (this.cur < this.names.length) {
|
|
||||||
const entrie = this.ctx.components[this.idxComponent].names.o[this.names[this.cur]];
|
|
||||||
this.subIt = new ArrayIt(this.ctx, entrie.type, entrie.sizes, entrie.offset, this.prefix + "." + this.names[this.cur], this.idxComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next() {
|
|
||||||
if (this.subIt) {
|
|
||||||
const res = this.subIt.next();
|
|
||||||
if (res == null) {
|
|
||||||
this.subIt = null;
|
|
||||||
this.cur++;
|
|
||||||
this._loadSubIt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.current();
|
|
||||||
}
|
|
||||||
|
|
||||||
current() {
|
|
||||||
if (this.subIt) {
|
|
||||||
return this.subIt.current();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
687
src/compiler.js
687
src/compiler.js
@@ -1,369 +1,227 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 0KIMS association.
|
Copyright 2018 0KIMS association.
|
||||||
|
|
||||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||||
|
|
||||||
circom is a free software: you can redistribute it and/or modify it
|
jaz is a free software: you can redistribute it and/or modify it
|
||||||
under the terms of the GNU General Public License as published by
|
under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
License for more details.
|
License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
const fs = require("fs");
|
||||||
const sONE = 0;
|
const path = require("path");
|
||||||
const build = require("./build");
|
const bigInt = require("big-integer");
|
||||||
const BuilderC = require("../ports/c/builder.js");
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
const BuilderWasm = require("../ports/wasm/builder.js");
|
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||||
const constructionPhase = require("./construction_phase");
|
const assert = require("assert");
|
||||||
const Ctx = require("./ctx");
|
const genCode = require("./gencode");
|
||||||
const utils = require("./utils");
|
const exec = require("./exec");
|
||||||
const buildR1cs = require("./r1csfile").buildR1cs;
|
const lc = require("./lcalgebra");
|
||||||
const BigArray = require("./bigarray");
|
|
||||||
const buildSyms = require("./buildsyms");
|
const util = require("util");
|
||||||
const {performance} = require("perf_hooks");
|
const fs_writeFile = util.promisify(fs.writeFile)
|
||||||
|
|
||||||
module.exports = compile;
|
module.exports = compile;
|
||||||
const measures = {};
|
|
||||||
|
|
||||||
function ms2String(v) {
|
const parser = require("../parser/jaz.js").parser;
|
||||||
v = Math.floor(v);
|
|
||||||
const ms = v % 1000;
|
const timeout = ms => new Promise(res => setTimeout(res, ms))
|
||||||
v = Math.floor(v/1000);
|
|
||||||
const secs = v % 60;
|
async function compile(srcFile, cFile) {
|
||||||
v = Math.floor(v/60);
|
const fullFileName = srcFile;
|
||||||
const mins = v % 60;
|
const fullFilePath = path.dirname(fullFileName);
|
||||||
v = Math.floor(v/60);
|
|
||||||
const hours = v % 24;
|
const src = fs.readFileSync(fullFileName, "utf8");
|
||||||
const days = Math.floor(v/24);
|
const ast = parser.parse(src);
|
||||||
let S = "";
|
|
||||||
if (days) S = S + days + "D ";
|
assert(ast.type == "BLOCK");
|
||||||
if ((S!="")||(hours)) S = S + hours.toString().padStart(2, "0") + ":";
|
|
||||||
if ((S!="")||(mins)) S = S + mins.toString().padStart(2, "0") + ":";
|
const ctx = {
|
||||||
if ((S!="")||(secs)) S = S + secs.toString().padStart(2, "0");
|
scopes: [{}],
|
||||||
S+=".";
|
signals: {
|
||||||
S = S + ms.toString().padStart(3, "0");
|
one: {
|
||||||
return S;
|
fullName: "one",
|
||||||
|
value: bigInt(1),
|
||||||
|
equivalence: "",
|
||||||
|
direction: ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
currentComponent: "",
|
||||||
|
constraints: [],
|
||||||
|
components: {},
|
||||||
|
templates: {},
|
||||||
|
functions: {},
|
||||||
|
functionParams: {},
|
||||||
|
filePath: fullFilePath,
|
||||||
|
fileName: fullFileName
|
||||||
|
};
|
||||||
|
|
||||||
async function compile(srcFile, options) {
|
exec(ctx, ast);
|
||||||
options.prime = options.prime || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
||||||
if (!options) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
if (typeof options.reduceConstraints === "undefined") {
|
|
||||||
options.reduceConstraints = true;
|
|
||||||
}
|
|
||||||
const ctx = new Ctx(options.prime);
|
|
||||||
ctx.verbose= options.verbose || false;
|
|
||||||
ctx.mainComponent = options.mainComponent || "main";
|
|
||||||
ctx.newThreadTemplates = options.newThreadTemplates;
|
|
||||||
|
|
||||||
measures.constructionPhase = -performance.now();
|
|
||||||
constructionPhase(ctx, srcFile);
|
|
||||||
measures.constructionPhase += performance.now();
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length);
|
|
||||||
if (ctx.verbose) console.log("NSignals Before: "+ctx.signals.length);
|
|
||||||
|
|
||||||
if (ctx.error) {
|
|
||||||
throw(ctx.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.getComponentIdx(ctx.mainComponent)<0) {
|
|
||||||
throw new Error("A main component must be defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("Classify Signals");
|
|
||||||
measures.classifySignals = -performance.now();
|
|
||||||
classifySignals(ctx);
|
classifySignals(ctx);
|
||||||
measures.classifySignals += performance.now();
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("Reduce Constants");
|
|
||||||
measures.reduceConstants = -performance.now();
|
|
||||||
reduceConstants(ctx);
|
reduceConstants(ctx);
|
||||||
measures.reduceConstants += performance.now();
|
|
||||||
|
|
||||||
if (options.reduceConstraints) {
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("Reduce Constraints");
|
|
||||||
// Repeat while reductions are performed
|
// Repeat while reductions are performed
|
||||||
/*
|
|
||||||
let oldNConstrains = -1;
|
let oldNConstrains = -1;
|
||||||
while (ctx.constraints.length != oldNConstrains) {
|
while (ctx.constraints.length != oldNConstrains) {
|
||||||
if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length);
|
|
||||||
oldNConstrains = ctx.constraints.length;
|
oldNConstrains = ctx.constraints.length;
|
||||||
reduceConstrains(ctx);
|
reduceConstrains(ctx);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
measures.reduceConstraints = -performance.now();
|
|
||||||
await reduceConstrains(ctx);
|
|
||||||
measures.reduceConstraints += performance.now();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
|
|
||||||
|
|
||||||
measures.generateWitnessNames = -performance.now();
|
|
||||||
generateWitnessNames(ctx);
|
generateWitnessNames(ctx);
|
||||||
measures.generateWitnessNames += performance.now();
|
|
||||||
|
|
||||||
if (ctx.error) {
|
if (ctx.error) {
|
||||||
throw(ctx.error);
|
throw(ctx.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.r1csFileName) {
|
ctx.scopes = [{}];
|
||||||
measures.generateR1cs = -performance.now();
|
|
||||||
await buildR1cs(ctx, options.r1csFileName);
|
|
||||||
measures.generateR1cs += performance.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const mainCode = genCode(ctx,ast);
|
||||||
if (ctx.error) throw(ctx.error);
|
if (ctx.error) throw(ctx.error);
|
||||||
|
|
||||||
delete ctx.constraints; // Liberate memory.
|
if (cFile) {
|
||||||
|
await fs_writeFile(cFile, mainCode);
|
||||||
if (options.cSourceFile) {
|
|
||||||
if (ctx.verbose) console.log("Generating c...");
|
|
||||||
measures.generateC = -performance.now();
|
|
||||||
ctx.builder = new BuilderC(options.prime, ctx.verbose);
|
|
||||||
build(ctx);
|
|
||||||
await ctx.builder.build(options.cSourceFile, options.dataFile);
|
|
||||||
measures.generateC += performance.now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.error) throw(ctx.error);
|
const def = buildCircuitDef(ctx, mainCode);
|
||||||
|
|
||||||
if ((options.wasmFile)||(options.watFile)) {
|
return def;
|
||||||
if (ctx.verbose) console.log("Generating wasm...");
|
|
||||||
measures.generateWasm = -performance.now();
|
|
||||||
ctx.builder = new BuilderWasm(options.prime);
|
|
||||||
build(ctx);
|
|
||||||
if (options.wasmFile) {
|
|
||||||
await ctx.builder.build(options.wasmFile, "wasm");
|
|
||||||
}
|
}
|
||||||
if (options.watFile) {
|
|
||||||
await ctx.builder.build(options.watFile, "wat");
|
|
||||||
}
|
|
||||||
measures.generateWasm += performance.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
// const mainCode = gen(ctx,ast);
|
|
||||||
if (ctx.error) throw(ctx.error);
|
|
||||||
|
|
||||||
if (options.symWriteStream) {
|
|
||||||
measures.generateSyms = -performance.now();
|
|
||||||
const rdStream = buildSyms(ctx);
|
|
||||||
rdStream.pipe(options.symWriteStream);
|
|
||||||
measures.generateSyms += performance.now();
|
|
||||||
|
|
||||||
await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
|
|
||||||
}
|
|
||||||
|
|
||||||
// const def = buildCircuitDef(ctx, mainCode);
|
|
||||||
|
|
||||||
if (ctx.verbose) {
|
|
||||||
for (let [mStr, mValue] of Object.entries(measures)) {
|
|
||||||
console.log(mStr + ": " + ms2String(mValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function classifySignals(ctx) {
|
function classifySignals(ctx) {
|
||||||
|
|
||||||
const ERROR = 0xFFFF;
|
|
||||||
|
|
||||||
function priorize(t1, t2) {
|
function priorize(t1, t2) {
|
||||||
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
if ((t1 == "error") || (t2=="error")) return "error";
|
||||||
if (t1 == ctx.stINTERNAL) {
|
if (t1 == "internal") {
|
||||||
return t2;
|
return t2;
|
||||||
} else if (t2==ctx.stINTERNAL) {
|
} else if (t2=="internal") {
|
||||||
return t1;
|
return t1;
|
||||||
}
|
}
|
||||||
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
if ((t1 == "one") || (t2 == "one")) return "one";
|
||||||
if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
|
if ((t1 == "constant") || (t2 == "constant")) return "constant";
|
||||||
if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
|
if (t1!=t2) return "error";
|
||||||
if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
|
|
||||||
if (t1!=t2) return ERROR;
|
|
||||||
return t1;
|
return t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First classify the signals
|
// First classify the signals
|
||||||
for (let s=0; s<ctx.signals.length; s++) {
|
for (let s in ctx.signals) {
|
||||||
if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
|
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let tAll = ctx.stINTERNAL;
|
let tAll = "internal";
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
let end = false;
|
let end = false;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
let t = lSignal.c || ctx.stINTERNAL;
|
let t = lSignal.category || "internal";
|
||||||
if (s == 0) {
|
if (s == "one") {
|
||||||
t = ctx.stONE;
|
t = "one";
|
||||||
} else if (lSignal.o & ctx.MAIN) {
|
} else if (lSignal.value) {
|
||||||
if (lSignal.o & ctx.IN) {
|
t = "constant";
|
||||||
if (lSignal.o & ctx.PRV) {
|
} else if (lSignal.component=="main") {
|
||||||
t = ctx.stPRVINPUT;
|
if (lSignal.direction == "IN") {
|
||||||
|
if (lSignal.private) {
|
||||||
|
t = "prvInput";
|
||||||
} else {
|
} else {
|
||||||
t = ctx.stPUBINPUT;
|
t = "pubInput";
|
||||||
}
|
}
|
||||||
} else if (lSignal.o & ctx.OUT) {
|
} else if (lSignal.direction == "OUT") {
|
||||||
t = ctx.stOUTPUT;
|
t = "output";
|
||||||
}
|
}
|
||||||
} else if (utils.isDefined(lSignal.v)) {
|
|
||||||
t = ctx.stCONSTANT;
|
|
||||||
}
|
}
|
||||||
tAll = priorize(t,tAll);
|
tAll = priorize(t,tAll);
|
||||||
if (lSignal.e>=0) {
|
if (lSignal.equivalence) {
|
||||||
lSignal = ctx.signals[lSignal.e];
|
lSignal = ctx.signals[lSignal.equivalence];
|
||||||
} else {
|
} else {
|
||||||
end=true;
|
end=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tAll == ERROR) {
|
if (tAll == "error") {
|
||||||
throw new Error("Incompatible types in signal: " + s);
|
throw new Error("Incompatible types in signal: " + s);
|
||||||
}
|
}
|
||||||
lSignal.c = tAll;
|
lSignal.category = tAll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function generateWitnessNames(ctx) {
|
function generateWitnessNames(ctx) {
|
||||||
const totals = {};
|
|
||||||
totals[ctx.stONE] = 0;
|
const totals = {
|
||||||
totals[ctx.stOUTPUT] = 0;
|
"output": 0,
|
||||||
totals[ctx.stPUBINPUT] = 0;
|
"pubInput": 0,
|
||||||
totals[ctx.stPRVINPUT] = 0;
|
"one": 0,
|
||||||
totals[ctx.stINTERNAL] = 0;
|
"prvInput": 0,
|
||||||
totals[ctx.stDISCARDED] = 0;
|
"internal": 0,
|
||||||
totals[ctx.stCONSTANT] = 0;
|
"constant": 0,
|
||||||
|
};
|
||||||
const ids = {};
|
const ids = {};
|
||||||
|
|
||||||
|
const counted = {};
|
||||||
|
|
||||||
// First classify the signals
|
// First classify the signals
|
||||||
for (let s=0; s<ctx.signals.length; s++) {
|
for (let s in ctx.signals) {
|
||||||
|
|
||||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
|
|
||||||
|
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
|
while (lSignal.equivalence) lSignal = ctx.signals[lSignal.equivalence];
|
||||||
|
|
||||||
if (!( lSignal.o & ctx.COUNTED) ) {
|
if (!counted[lSignal.fullName]) {
|
||||||
lSignal.o |= ctx.COUNTED;
|
counted[lSignal.fullName] = true;
|
||||||
totals[lSignal.c] ++;
|
totals[lSignal.category] ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ids[ctx.stONE] = 0;
|
ids["one"] = 0;
|
||||||
ids[ctx.stOUTPUT] = 1;
|
ids["output"] = 1;
|
||||||
ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
|
ids["pubInput"] = ids["output"] + totals["output"];
|
||||||
ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
|
ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
|
||||||
ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
|
ids["internal"] = ids["prvInput"] + totals["prvInput"];
|
||||||
ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
|
ids["constant"] = ids["internal"] + totals["internal"];
|
||||||
ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
|
const nSignals = ids["constant"] + totals["constant"];
|
||||||
const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
|
|
||||||
|
|
||||||
for (let s=0; s<ctx.signals.length; s++) {
|
ctx.signalNames = new Array(nSignals);
|
||||||
|
for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
|
||||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
|
ctx.signalName2Idx = {};
|
||||||
|
|
||||||
|
for (let s in ctx.signals) {
|
||||||
const signal = ctx.signals[s];
|
const signal = ctx.signals[s];
|
||||||
let lSignal = signal;
|
let lSignal = signal;
|
||||||
while (lSignal.e>=0) {
|
while (lSignal.equivalence) {
|
||||||
lSignal = ctx.signals[lSignal.e];
|
lSignal = ctx.signals[lSignal.equivalence];
|
||||||
}
|
}
|
||||||
if ( typeof(lSignal.id) === "undefined" ) {
|
if ( typeof(lSignal.id) === "undefined" ) {
|
||||||
lSignal.id = ids[lSignal.c] ++;
|
lSignal.id = ids[lSignal.category] ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
signal.id = lSignal.id;
|
signal.id = lSignal.id;
|
||||||
|
ctx.signalNames[signal.id].push(signal.fullName);
|
||||||
|
ctx.signalName2Idx[signal.fullName] = signal.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.totals = totals;
|
ctx.totals = totals;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reduceConstants(ctx) {
|
function reduceConstants(ctx) {
|
||||||
const newConstraints = new BigArray();
|
const newConstraints = [];
|
||||||
for (let i=0; i<ctx.constraints.length; i++) {
|
for (let i=0; i<ctx.constraints.length; i++) {
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
|
const c = lc.canonize(ctx, ctx.constraints[i]);
|
||||||
const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
|
if (!lc.isZero(c)) {
|
||||||
if (!ctx.lc.isZero(c)) {
|
|
||||||
newConstraints.push(c);
|
newConstraints.push(c);
|
||||||
}
|
}
|
||||||
delete ctx.constraints[i];
|
|
||||||
}
|
}
|
||||||
ctx.constraints = newConstraints;
|
ctx.constraints = newConstraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reduceConstrains(ctx) {
|
function reduceConstrains(ctx) {
|
||||||
const sig2constraint = new BigArray();
|
const newConstraints = [];
|
||||||
let removedSignals = new BigArray();
|
|
||||||
let nRemoved;
|
|
||||||
let lIdx;
|
|
||||||
|
|
||||||
|
|
||||||
let possibleConstraints = new BigArray(ctx.constraints.length);
|
|
||||||
let nextPossibleConstraints;
|
|
||||||
for (let i=0; i<ctx.constraints.length; i++) {
|
for (let i=0; i<ctx.constraints.length; i++) {
|
||||||
if ((ctx.verbose)&&(i%100000 == 0)) console.log(`indexing constraints: ${i}/${ctx.constraints.length}`);
|
|
||||||
|
|
||||||
const insertedSig = { 0: true}; // Do not insert one.
|
|
||||||
const c = ctx.constraints[i];
|
const c = ctx.constraints[i];
|
||||||
for (let s in c.a.coefs) {
|
|
||||||
if (!insertedSig[s]) {
|
|
||||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
|
||||||
sig2constraint[s][i] = true;
|
|
||||||
insertedSig[s] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let s in c.b.coefs) {
|
|
||||||
if (!insertedSig[s]) {
|
|
||||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
|
||||||
sig2constraint[s][i] = true;
|
|
||||||
insertedSig[s] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let s in c.c.coefs) {
|
|
||||||
if (!insertedSig[s]) {
|
|
||||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
|
||||||
sig2constraint[s][i] = true;
|
|
||||||
insertedSig[s] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
possibleConstraints[i] = ctx.constraints.length - i -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let totalRemoved = 0;
|
|
||||||
while (possibleConstraints.length >0) {
|
|
||||||
if (possibleConstraints.length>1<<20) {
|
|
||||||
nextPossibleConstraints = new BigArray();
|
|
||||||
} else {
|
|
||||||
nextPossibleConstraints = {};
|
|
||||||
}
|
|
||||||
removedSignals = {};
|
|
||||||
nRemoved = 0;
|
|
||||||
lIdx = {};
|
|
||||||
for (let i=0;i<possibleConstraints.length;i++) {
|
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
|
||||||
await Promise.resolve();
|
|
||||||
console.log(`reducing constraints: ${i}/${possibleConstraints.length} reduced: ${nRemoved}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const c = ctx.constraints[possibleConstraints[i]];
|
|
||||||
if (!c) continue;
|
|
||||||
|
|
||||||
// Limit of number of lelements removed per step
|
|
||||||
if (nRemoved>5000000) {
|
|
||||||
nextPossibleConstraints[possibleConstraints[i]] = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap a and b if b has more variables.
|
// Swap a and b if b has more variables.
|
||||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||||
@@ -374,204 +232,183 @@ async function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
// Mov to C if possible.
|
// Mov to C if possible.
|
||||||
if (isConstant(c.a)) {
|
if (isConstant(c.a)) {
|
||||||
const ct = {t: "N", v: c.a.coefs[sONE]};
|
const ct = {type: "NUMBER", value: c.a.values["one"]};
|
||||||
c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
|
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
||||||
c.a = { t: "LC", coefs: {} };
|
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||||
c.b = { t: "LC", coefs: {} };
|
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||||
}
|
}
|
||||||
if (isConstant(c.b)) {
|
if (isConstant(c.b)) {
|
||||||
const ct = {t: "N", v: c.b.coefs[sONE]};
|
const ct = {type: "NUMBER", value: c.b.values["one"]};
|
||||||
c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
|
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
||||||
c.a = { t: "LC", coefs: {} };
|
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||||
c.b = { t: "LC", coefs: {} };
|
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
|
if (lc.isZero(c.a) || lc.isZero(c.b)) {
|
||||||
const freeC = substituteRemoved(c.c);
|
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||||
const isolatedSignal = getFirstInternalSignal(ctx, freeC);
|
|
||||||
if (isolatedSignal) {
|
if (isolatedSignal) {
|
||||||
removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
|
const isolatedSignalEquivalence = {
|
||||||
if (lIdx[isolatedSignal]) {
|
type: "LINEARCOMBINATION",
|
||||||
const sigs = Object.keys(lIdx[isolatedSignal]);
|
values: {}
|
||||||
|
|
||||||
for (let k=0; k<sigs.length; k++) {
|
|
||||||
const s = sigs[k];
|
|
||||||
const oldLC = removedSignals[s];
|
|
||||||
removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
|
|
||||||
if (oldLC !== removedSignals[s]) addTolIdx(removedSignals[s], s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
|
|
||||||
ctx.constraints[possibleConstraints[i]] = null;
|
|
||||||
nRemoved ++;
|
|
||||||
|
|
||||||
delete lIdx[isolatedSignal];
|
|
||||||
|
|
||||||
const cts = Object.keys(sig2constraint[isolatedSignal]);
|
|
||||||
for (let k=0; k<cts.length; k++) {
|
|
||||||
if (ctx.constraints[cts[k]]) nextPossibleConstraints[cts[k]] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextPossibleConstraints.getKeys) {
|
|
||||||
nextPossibleConstraints = nextPossibleConstraints.getKeys();
|
|
||||||
} else {
|
|
||||||
nextPossibleConstraints = Object.keys(nextPossibleConstraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i=0; i<nextPossibleConstraints.length;i++) {
|
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
|
||||||
await Promise.resolve();
|
|
||||||
console.log(`substituting constraints: ${i}/${nextPossibleConstraints.length}`);
|
|
||||||
}
|
|
||||||
const c = ctx.constraints[nextPossibleConstraints[i]];
|
|
||||||
if (c) {
|
|
||||||
const nc = {
|
|
||||||
a: substituteRemoved(c.a, nextPossibleConstraints[i]),
|
|
||||||
b: substituteRemoved(c.b, nextPossibleConstraints[i]),
|
|
||||||
c: substituteRemoved(c.c, nextPossibleConstraints[i])
|
|
||||||
};
|
};
|
||||||
if (ctx.lc.isZero(nc)) {
|
const invCoef = c.c.values[isolatedSignal].modInv(__P__);
|
||||||
delete ctx.constraints[nextPossibleConstraints[i]];
|
for (const s in c.c.values) {
|
||||||
} else {
|
if (s != isolatedSignal) {
|
||||||
ctx.constraints[nextPossibleConstraints[i]] = nc;
|
const v = __P__.minus(c.c.values[s]).times(invCoef).mod(__P__);
|
||||||
|
if (!v.isZero()) {
|
||||||
|
isolatedSignalEquivalence.values[s] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removedSignalsList = Object.keys(removedSignals);
|
for (let j=0; j<ctx.constraints.length; j++ ) {
|
||||||
|
const c2 = ctx.constraints[j];
|
||||||
for (let i=0; i<removedSignalsList.length; i++) {
|
if (i!=j) {
|
||||||
if ((ctx.verbose )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
|
lc.substitute(c2, isolatedSignal, isolatedSignalEquivalence);
|
||||||
const s = removedSignalsList[i];
|
|
||||||
|
|
||||||
let lSignal = ctx.signals[s];
|
|
||||||
while (lSignal.e>=0) {
|
|
||||||
lSignal = ctx.signals[lSignal.e];
|
|
||||||
}
|
|
||||||
|
|
||||||
sig2constraint[s] = null;
|
|
||||||
lSignal.c = ctx.stDISCARDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
possibleConstraints = new BigArray();
|
|
||||||
// Reverse
|
|
||||||
for (let i=0; i<nextPossibleConstraints.length; i++) {
|
|
||||||
possibleConstraints[i] = nextPossibleConstraints[nextPossibleConstraints.length -1 -i];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
possibleConstraints = nextPossibleConstraints;
|
|
||||||
|
|
||||||
totalRemoved += nRemoved;
|
|
||||||
if (ctx.verbose) console.log(`Removed: ${totalRemoved} TotalConstraints: ${ctx.constraints.length}` );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let o=0;
|
|
||||||
for (let i=0; i<ctx.constraints.length;i++) {
|
|
||||||
if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
|
|
||||||
if (ctx.constraints[i]) {
|
|
||||||
if (!ctx.lc.isZero(ctx.constraints[i])) {
|
|
||||||
ctx.constraints[o] = ctx.constraints[i];
|
|
||||||
o++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.a={ type: "LINEARCOMBINATION", values: {} };
|
||||||
|
c.b={ type: "LINEARCOMBINATION", values: {} };
|
||||||
|
c.c={ type: "LINEARCOMBINATION", values: {} };
|
||||||
|
isolatedSignal.category = "constant";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.constraints.length = o;
|
if (!lc.isZero(c)) {
|
||||||
|
newConstraints.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.constraints = newConstraints;
|
||||||
|
|
||||||
function getFirstInternalSignal(ctx, l) {
|
function getFirstInternalSignal(ctx, l) {
|
||||||
for (let k in l.coefs) {
|
for (let k in l.values) {
|
||||||
k = Number(k);
|
|
||||||
const signal = ctx.signals[k];
|
const signal = ctx.signals[k];
|
||||||
if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
|
if (signal.category == "internal") return k;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isolateSignal(lc, s) {
|
|
||||||
const eq = {
|
|
||||||
t: "LC",
|
|
||||||
coefs: {}
|
|
||||||
};
|
|
||||||
const invCoef = ctx.F.inv(lc.coefs[s]);
|
|
||||||
for (const k in lc.coefs) {
|
|
||||||
if (k != s) {
|
|
||||||
const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef);
|
|
||||||
if (!ctx.F.isZero(v)) {
|
|
||||||
eq.coefs[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return eq;
|
|
||||||
}
|
|
||||||
|
|
||||||
function substituteRemoved(lc, idxConstraint) {
|
|
||||||
const newLc = ctx.lc._clone(lc);
|
|
||||||
for (let k in lc.coefs) {
|
|
||||||
if (removedSignals[k]) {
|
|
||||||
delete newLc.coefs[k];
|
|
||||||
for (let k2 in removedSignals[k].coefs) {
|
|
||||||
const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]);
|
|
||||||
if (!ctx.F.isZero(newP)) {
|
|
||||||
if (newLc.coefs[k2]) {
|
|
||||||
newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
|
|
||||||
if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
|
|
||||||
} else {
|
|
||||||
newLc.coefs[k2] = newP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((typeof idxConstraint != "undefined")&&(k2!=0)) {
|
|
||||||
if (!sig2constraint[k2]) sig2constraint[k2] = {};
|
|
||||||
sig2constraint[k2][idxConstraint] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newLc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function substitute(lc, s, eq) {
|
|
||||||
if (!lc.coefs[s]) return lc;
|
|
||||||
const newLc = ctx.lc._clone(lc);
|
|
||||||
delete newLc.coefs[s];
|
|
||||||
for (let k2 in eq.coefs) {
|
|
||||||
const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]);
|
|
||||||
if (!ctx.F.isZero(newP)) {
|
|
||||||
if (newLc.coefs[k2]) {
|
|
||||||
newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
|
|
||||||
if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
|
|
||||||
} else {
|
|
||||||
newLc.coefs[k2] = newP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newLc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isConstant(l) {
|
function isConstant(l) {
|
||||||
for (let k in l.coefs) {
|
for (let k in l.values) {
|
||||||
if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
|
if ((k != "one") && (!l.values[k].isZero())) return false;
|
||||||
}
|
}
|
||||||
if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
|
if (!l.values["one"] || l.values["one"].isZero()) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTolIdx(lc, newS) {
|
}
|
||||||
const sigs = Object.keys(lc.coefs);
|
|
||||||
for (let k=0; k<sigs.length; k++) {
|
|
||||||
const s = sigs[k];
|
function buildCircuitDef(ctx, mainCode) {
|
||||||
if (s) {
|
const res = {
|
||||||
if (!lIdx[s]) lIdx[s] = {};
|
mainCode: mainCode
|
||||||
lIdx[s][newS] = true;
|
};
|
||||||
|
res.signalName2Idx = ctx.signalName2Idx;
|
||||||
|
|
||||||
|
res.components = [];
|
||||||
|
res.componentName2Idx = {};
|
||||||
|
for (let c in ctx.components) {
|
||||||
|
const idCoponent = res.components.length;
|
||||||
|
res.components.push({
|
||||||
|
name: c,
|
||||||
|
params: ctx.components[c].params,
|
||||||
|
template: ctx.components[c].template,
|
||||||
|
inputSignals: 0
|
||||||
|
});
|
||||||
|
res.componentName2Idx[c] = idCoponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.signals = new Array(ctx.signalNames.length);
|
||||||
|
for (let i=0; i<ctx.signalNames.length; i++) {
|
||||||
|
res.signals[i] = {
|
||||||
|
names: ctx.signalNames[i],
|
||||||
|
triggerComponents: []
|
||||||
|
};
|
||||||
|
ctx.signalNames[i].map( (fullName) => {
|
||||||
|
const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
|
||||||
|
if (ctx.signals[fullName].direction == "IN") {
|
||||||
|
res.signals[i].triggerComponents.push(idComponet);
|
||||||
|
res.components[idComponet].inputSignals++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.constraints = buildConstraints(ctx);
|
||||||
|
|
||||||
|
res.templates = ctx.templates;
|
||||||
|
|
||||||
|
res.functions = {};
|
||||||
|
for (let f in ctx.functions) {
|
||||||
|
res.functions[f] = {
|
||||||
|
params: ctx.functionParams[f],
|
||||||
|
func: ctx.functions[f]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
res.nPrvInputs = ctx.totals.prvInput;
|
||||||
|
res.nPubInputs = ctx.totals.pubInput;
|
||||||
|
res.nInputs = res.nPrvInputs + res.nPubInputs;
|
||||||
|
res.nOutputs = ctx.totals.output;
|
||||||
|
res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
|
||||||
|
res.nConstants = ctx.totals.constant;
|
||||||
|
res.nSignals = res.nVars + res.nConstants;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Build constraints
|
||||||
|
|
||||||
|
A constraint like this
|
||||||
|
|
||||||
|
[s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0 ] = 0
|
||||||
|
[ 5*s2 + 6*s3] * [ s2 + ] - [s0 + 2* s2] = 0
|
||||||
|
[s1 + s3] * [ s2 + 5*s3] - [s4 ] = 0
|
||||||
|
|
||||||
|
is converted to
|
||||||
|
|
||||||
|
[
|
||||||
|
[{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1" }],
|
||||||
|
[{ "2":"5","3":"6"} , {"2":"1" } , {"0":"1", "2":"2"}],
|
||||||
|
[{"1":"1", "3":"1"} , {"2":"1","3":"5"} , {"4":"1" }]
|
||||||
|
]
|
||||||
|
^ ^ ^
|
||||||
|
| | |
|
||||||
|
A B C
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function buildConstraints(ctx) {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
function fillLC(dst, src) {
|
||||||
|
if (src.type != "LINEARCOMBINATION") throw new Error("Constraint is not a LINEARCOMBINATION");
|
||||||
|
for (let s in src.values) {
|
||||||
|
const v = src.values[s].toString();
|
||||||
|
const id = ctx.signalName2Idx[s];
|
||||||
|
dst[id] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i=0; i<ctx.constraints.length; i++) {
|
||||||
|
const A = {};
|
||||||
|
const B = {};
|
||||||
|
const C = {};
|
||||||
|
|
||||||
|
fillLC(A, ctx.constraints[i].a);
|
||||||
|
fillLC(B, ctx.constraints[i].b);
|
||||||
|
fillLC(C, lc.negate(ctx.constraints[i].c));
|
||||||
|
|
||||||
|
res.push([A,B,C]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function generateCCode(ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
228
src/ctx.js
228
src/ctx.js
@@ -1,228 +0,0 @@
|
|||||||
const BigArray = require("./bigarray.js");
|
|
||||||
const F1Field = require("ffjavascript").F1Field;
|
|
||||||
|
|
||||||
class TableName {
|
|
||||||
constructor (ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.o = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
_allocElement(name, _sizes, type) {
|
|
||||||
const sizes = _sizes || [];
|
|
||||||
let l = 1;
|
|
||||||
for (let i=0; i<sizes.length; i++) {
|
|
||||||
l = l*sizes[i];
|
|
||||||
}
|
|
||||||
this.o[name] = {
|
|
||||||
sizes: sizes,
|
|
||||||
type: type
|
|
||||||
};
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
addSignal(name, sizes) {
|
|
||||||
const l = this._allocElement(name, sizes, "S");
|
|
||||||
const o = this.ctx.nSignals;
|
|
||||||
this.o[name].offset = o;
|
|
||||||
this.ctx.nSignals += l;
|
|
||||||
if (l>1) {
|
|
||||||
return [o, o+l];
|
|
||||||
} else {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addComponent(name, sizes) {
|
|
||||||
const l = this._allocElement(name, sizes, "C");
|
|
||||||
const o = this.ctx.nComponents;
|
|
||||||
this.o[name].offset = o;
|
|
||||||
this.ctx.nComponents += l;
|
|
||||||
if (l>1) {
|
|
||||||
return [o, o+l];
|
|
||||||
} else {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getElement(name, _sels, type) {
|
|
||||||
const sels = _sels || [];
|
|
||||||
const s = this.o[name];
|
|
||||||
if (!s) return -1;
|
|
||||||
if (s.type != type) return -1;
|
|
||||||
if (sels.length > s.sizes.length) return -1;
|
|
||||||
let l=1;
|
|
||||||
for (let i = s.sizes.length-1; i>sels.length; i--) {
|
|
||||||
l = l*s.sizes[i];
|
|
||||||
}
|
|
||||||
let o =0;
|
|
||||||
let p=1;
|
|
||||||
for (let i=sels.length-1; i>=0; i--) {
|
|
||||||
if (sels[i] > s.sizes[i]) return -1; // Out of range
|
|
||||||
if (sels[i] < 0) return -1; // Out of range
|
|
||||||
o += p*sels[i];
|
|
||||||
p *= s.sizes[i];
|
|
||||||
}
|
|
||||||
if (l>1) {
|
|
||||||
return [s.offset + o, s.offset + o + l];
|
|
||||||
} else {
|
|
||||||
return s.offset + o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSignalIdx(name, sels) {
|
|
||||||
return this._getElement(name, sels, "S");
|
|
||||||
}
|
|
||||||
|
|
||||||
getComponentIdx(name, sels) {
|
|
||||||
return this._getElement(name, sels, "C");
|
|
||||||
}
|
|
||||||
|
|
||||||
getSizes(name) {
|
|
||||||
return this.o[name].sels;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = class Ctx {
|
|
||||||
|
|
||||||
constructor(p) {
|
|
||||||
|
|
||||||
this.F = new F1Field(p);
|
|
||||||
|
|
||||||
this.stONE = 1;
|
|
||||||
this.stOUTPUT = 2;
|
|
||||||
this.stPUBINPUT = 3;
|
|
||||||
this.stPRVINPUT = 4;
|
|
||||||
this.stINTERNAL = 5;
|
|
||||||
this.stDISCARDED = 6;
|
|
||||||
this.stCONSTANT = 7;
|
|
||||||
|
|
||||||
this.IN = 0x01;
|
|
||||||
this.OUT = 0x02;
|
|
||||||
this.PRV = 0x04;
|
|
||||||
this.ONE = 0x08;
|
|
||||||
this.MAIN = 0x10;
|
|
||||||
this.COUNTED = 0x20;
|
|
||||||
|
|
||||||
this.scopes = [{}];
|
|
||||||
this.signals = new BigArray();
|
|
||||||
|
|
||||||
this.currentComponent= -1;
|
|
||||||
this.constraints= new BigArray();
|
|
||||||
this.components= new BigArray();
|
|
||||||
this.templates= {};
|
|
||||||
this.functions= {};
|
|
||||||
this.functionParams= {};
|
|
||||||
this.nSignals = 0;
|
|
||||||
this.nComponents =0;
|
|
||||||
this.names = new TableName(this);
|
|
||||||
this.main=false;
|
|
||||||
this.error = null;
|
|
||||||
this.warnings = [];
|
|
||||||
|
|
||||||
const oneIdx = this.addSignal("one");
|
|
||||||
this.signals[oneIdx] = {
|
|
||||||
v: this.F.one,
|
|
||||||
o: this.ONE,
|
|
||||||
e: -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.uniqueNames = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
addSignal(name, sizes) {
|
|
||||||
if (this.currentComponent>=0) {
|
|
||||||
return this.components[this.currentComponent].names.addSignal(name, sizes);
|
|
||||||
} else {
|
|
||||||
return this.names.addSignal(name, sizes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addComponent(name, sizes) {
|
|
||||||
if (this.currentComponent>=0) {
|
|
||||||
return this.components[this.currentComponent].names.addComponent(name, sizes);
|
|
||||||
} else {
|
|
||||||
return this.names.addComponent(name, sizes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSignalIdx(name, sels) {
|
|
||||||
if (this.currentComponent>=0) {
|
|
||||||
return this.components[this.currentComponent].names.getSignalIdx(name, sels);
|
|
||||||
} else {
|
|
||||||
return this.names.getSignalIdx(name, sels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getComponentIdx(name, sels) {
|
|
||||||
if (this.currentComponent>=0) {
|
|
||||||
return this.components[this.currentComponent].names.getComponentIdx(name, sels);
|
|
||||||
} else {
|
|
||||||
return this.names.getComponentIdx(name, sels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSizes(name) {
|
|
||||||
if (this.currentComponent>=0) {
|
|
||||||
return this.components[this.currentComponent].names.getSizes(name);
|
|
||||||
} else {
|
|
||||||
return this.names.getSizes(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newTableName() {
|
|
||||||
return new TableName(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildErr(ast, errStr) {
|
|
||||||
if (typeof ast == "string") {
|
|
||||||
ast = null;
|
|
||||||
errStr = ast;
|
|
||||||
}
|
|
||||||
if (ast) {
|
|
||||||
return {
|
|
||||||
pos: {
|
|
||||||
first_line: ast.first_line,
|
|
||||||
first_column: ast.first_column,
|
|
||||||
last_line: ast.last_line,
|
|
||||||
last_column: ast.last_column
|
|
||||||
},
|
|
||||||
errStr: errStr,
|
|
||||||
ast: ast,
|
|
||||||
message: errStr,
|
|
||||||
errFile: this.fileName
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
errStr: errStr,
|
|
||||||
message: errStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throwError(ast, errStr) {
|
|
||||||
const err = this._buildErr(ast, errStr);
|
|
||||||
this.error = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
logWarning(ast, errStr) {
|
|
||||||
const w = this._buildErr(ast, errStr);
|
|
||||||
this.warnings.push(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUniqueName(suggestedName) {
|
|
||||||
if (!suggestedName) {
|
|
||||||
suggestedName = "_tmp";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof(this.uniqueNames[suggestedName]) == "undefined") {
|
|
||||||
this.uniqueNames[suggestedName] = 1;
|
|
||||||
return suggestedName;
|
|
||||||
} else {
|
|
||||||
const name = suggestedName + "_" + this.uniqueNames[suggestedName];
|
|
||||||
this.uniqueNames[suggestedName]++;
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
981
src/exec.js
Normal file
981
src/exec.js
Normal file
@@ -0,0 +1,981 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 0KIMS association.
|
||||||
|
|
||||||
|
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||||
|
|
||||||
|
jaz is a free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
const bigInt = require("big-integer");
|
||||||
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
|
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||||
|
|
||||||
|
const lc = require("./lcalgebra");
|
||||||
|
const parser = require("../parser/jaz.js").parser;
|
||||||
|
|
||||||
|
/* TODO: Add lines information
|
||||||
|
|
||||||
|
function setLines(dst, first, last) {
|
||||||
|
last = last || first;
|
||||||
|
dst.first_line = first.first_line;
|
||||||
|
dst.first_column = first.first_column;
|
||||||
|
dst.last_line = last.last_line;
|
||||||
|
dst.last_column = last.last_column;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = exec;
|
||||||
|
|
||||||
|
|
||||||
|
function exec(ctx, ast) {
|
||||||
|
if (!ast) {
|
||||||
|
return error(ctx, ast, "Null AST");
|
||||||
|
}
|
||||||
|
if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) {
|
||||||
|
return ast;
|
||||||
|
} else if (ast.type == "VARIABLE") {
|
||||||
|
return execVariable(ctx, ast);
|
||||||
|
} else if (ast.type == "PIN") {
|
||||||
|
return execPin(ctx, ast);
|
||||||
|
} else if (ast.type == "OP") {
|
||||||
|
if (ast.op == "=") {
|
||||||
|
return execVarAssignement(ctx, ast);
|
||||||
|
} else if (ast.op == "<--") {
|
||||||
|
return execSignalAssign(ctx, ast);
|
||||||
|
} else if (ast.op == "<==") {
|
||||||
|
return execSignalAssignConstrain(ctx, ast);
|
||||||
|
} else if (ast.op == "===") {
|
||||||
|
return execConstrain(ctx, ast);
|
||||||
|
} else if (ast.op == "+=") {
|
||||||
|
return execVarAddAssignement(ctx, ast);
|
||||||
|
} else if (ast.op == "*=") {
|
||||||
|
return execVarMulAssignement(ctx, ast);
|
||||||
|
} else if (ast.op == "+") {
|
||||||
|
return execAdd(ctx, ast);
|
||||||
|
} else if (ast.op == "-") {
|
||||||
|
return execSub(ctx, ast);
|
||||||
|
} else if (ast.op == "UMINUS") {
|
||||||
|
return execUMinus(ctx, ast);
|
||||||
|
} else if (ast.op == "*") {
|
||||||
|
return execMul(ctx, ast);
|
||||||
|
} else if (ast.op == "%") {
|
||||||
|
return execMod(ctx, ast);
|
||||||
|
} else if (ast.op == "PLUSPLUSRIGHT") {
|
||||||
|
return execPlusPlusRight(ctx, ast);
|
||||||
|
} else if (ast.op == "PLUSPLUSLEFT") {
|
||||||
|
return execPlusPlusLeft(ctx, ast);
|
||||||
|
} else if (ast.op == "**") {
|
||||||
|
return execExp(ctx, ast);
|
||||||
|
} else if (ast.op == "&") {
|
||||||
|
return execBAnd(ctx, ast);
|
||||||
|
} else if (ast.op == "<<") {
|
||||||
|
return execShl(ctx, ast);
|
||||||
|
} else if (ast.op == ">>") {
|
||||||
|
return execShr(ctx, ast);
|
||||||
|
} else if (ast.op == "<") {
|
||||||
|
return execLt(ctx, ast);
|
||||||
|
} else if (ast.op == ">") {
|
||||||
|
return execGt(ctx, ast);
|
||||||
|
} else if (ast.op == "<=") {
|
||||||
|
return execLte(ctx, ast);
|
||||||
|
} else if (ast.op == ">=") {
|
||||||
|
return execGte(ctx, ast);
|
||||||
|
} else if (ast.op == "==") {
|
||||||
|
return execEq(ctx, ast);
|
||||||
|
} else if (ast.op == "?") {
|
||||||
|
return execTerCon(ctx, ast);
|
||||||
|
} else {
|
||||||
|
error(ctx, ast, "Invalid operation: " + ast.op);
|
||||||
|
}
|
||||||
|
} else if (ast.type == "DECLARE") {
|
||||||
|
if (ast.declareType == "COMPONENT") {
|
||||||
|
return execDeclareComponent(ctx, ast);
|
||||||
|
} else if ((ast.declareType == "SIGNALIN")||
|
||||||
|
(ast.declareType == "SIGNALOUT")||
|
||||||
|
(ast.declareType == "SIGNAL")) {
|
||||||
|
return execDeclareSignal(ctx, ast);
|
||||||
|
} else if (ast.declareType == "VARIABLE") {
|
||||||
|
return execDeclareVariable(ctx, ast);
|
||||||
|
} else {
|
||||||
|
error(ctx, ast, "Invalid declaration: " + ast.declareType);
|
||||||
|
}
|
||||||
|
} else if (ast.type == "FUNCTIONCALL") {
|
||||||
|
return execFunctionCall(ctx, ast);
|
||||||
|
} else if (ast.type == "BLOCK") {
|
||||||
|
return execBlock(ctx, ast);
|
||||||
|
} else if (ast.type == "FOR") {
|
||||||
|
return execFor(ctx, ast);
|
||||||
|
} else if (ast.type == "WHILE") {
|
||||||
|
return execWhile(ctx, ast);
|
||||||
|
} else if (ast.type == "IF") {
|
||||||
|
return execIf(ctx, ast);
|
||||||
|
} else if (ast.type == "RETURN") {
|
||||||
|
return execReturn(ctx, ast);
|
||||||
|
} else if (ast.type == "TEMPLATEDEF") {
|
||||||
|
return execTemplateDef(ctx, ast);
|
||||||
|
} else if (ast.type == "FUNCTIONDEF") {
|
||||||
|
return execFunctionDef(ctx, ast);
|
||||||
|
} else if (ast.type == "INCLUDE") {
|
||||||
|
return execInclude(ctx, ast);
|
||||||
|
} else if (ast.type == "ARRAY") {
|
||||||
|
return execArray(ctx, ast);
|
||||||
|
} else {
|
||||||
|
error(ctx, ast, "Invalid AST node type: " + ast.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(ctx, ast, errStr) {
|
||||||
|
ctx.error = {
|
||||||
|
pos: {
|
||||||
|
first_line: ast.first_line,
|
||||||
|
first_column: ast.first_column,
|
||||||
|
last_line: ast.last_line,
|
||||||
|
last_column: ast.last_column
|
||||||
|
},
|
||||||
|
errStr: errStr,
|
||||||
|
errFile: ctx.fileName,
|
||||||
|
ast: ast
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterateSelectors(ctx, sizes, baseName, fn) {
|
||||||
|
if (sizes.length == 0) {
|
||||||
|
return fn(baseName);
|
||||||
|
}
|
||||||
|
const res = [];
|
||||||
|
for (let i=0; i<sizes[0]; i++) {
|
||||||
|
res.push(iterateSelectors(ctx, sizes.slice(1), baseName+"["+i+"]", fn));
|
||||||
|
if (ctx.error) return null;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScope(ctx, name, selectors, value) {
|
||||||
|
let l = getScopeLevel(ctx, name);
|
||||||
|
if (l==-1) l= ctx.scopes.length-1;
|
||||||
|
|
||||||
|
if (selectors.length == 0) {
|
||||||
|
ctx.scopes[l][name] = value;
|
||||||
|
} else {
|
||||||
|
setScopeArray(ctx.scopes[l][name], selectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScopeArray(a, sels) {
|
||||||
|
if (sels.length == 1) {
|
||||||
|
a[sels[0]] = value;
|
||||||
|
} else {
|
||||||
|
setScopeArray(a[sels[0]], sels.slice(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScope(ctx, name, selectors) {
|
||||||
|
|
||||||
|
const sels = [];
|
||||||
|
if (selectors) {
|
||||||
|
for (let i=0; i< selectors.length; i++) {
|
||||||
|
const idx = exec(ctx, selectors[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
|
||||||
|
sels.push( idx.value.toJSNumber() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function select(v, s) {
|
||||||
|
s = s || [];
|
||||||
|
if (s.length == 0) return v;
|
||||||
|
return select(v[s[0]], s.slice(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||||
|
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScopeLevel(ctx, name) {
|
||||||
|
for (let i=ctx.scopes.length-1; i>=0; i--) {
|
||||||
|
if (ctx.scopes[i][name]) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execBlock(ctx, ast) {
|
||||||
|
for (let i=0; i<ast.statements.length; i++) {
|
||||||
|
exec(ctx, ast.statements[i]);
|
||||||
|
if (ctx.returnValue) return;
|
||||||
|
if (ctx.error) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execTemplateDef(ctx, ast) {
|
||||||
|
const scope = ctx.scopes[0]; // Lets put templates always in top scope.
|
||||||
|
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||||
|
if (getScope(ctx, ast.name)) {
|
||||||
|
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||||
|
}
|
||||||
|
scope[ast.name] = {
|
||||||
|
type: "TEMPLATE",
|
||||||
|
params: ast.params,
|
||||||
|
block: ast.block,
|
||||||
|
fileName: ctx.fileName,
|
||||||
|
filePath: ctx.filePath,
|
||||||
|
scopes: copyScope(ctx.scopes)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execFunctionDef(ctx, ast) {
|
||||||
|
const scope = ctx.scopes[0]; // Lets put functions always in top scope.
|
||||||
|
// const scope = ctx.scopes[ctx.scopes.length-1];
|
||||||
|
if (getScope(ctx, ast.name)) {
|
||||||
|
return error(ctx, ast, "Name already exists: "+ast.name);
|
||||||
|
}
|
||||||
|
ctx.functionParams[ast.name] = ast.params;
|
||||||
|
scope[ast.name] = {
|
||||||
|
type: "FUNCTION",
|
||||||
|
params: ast.params,
|
||||||
|
block: ast.block,
|
||||||
|
fileName: ctx.fileName,
|
||||||
|
filePath: ctx.filePath,
|
||||||
|
scopes: copyScope(ctx.scopes)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execDeclareComponent(ctx, ast) {
|
||||||
|
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||||
|
|
||||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
||||||
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||||
|
|
||||||
|
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||||
|
|
||||||
|
const sizes=[];
|
||||||
|
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||||
|
const size = exec(ctx, ast.name.selectors[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||||
|
|
||||||
|
sizes.push( size.value.toJSNumber() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||||
|
|
||||||
|
ctx.components[fullName] = "UNINSTANTIATED";
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "COMPONENT",
|
||||||
|
fullName: fullName
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "VARIABLE",
|
||||||
|
name: ast.name.name,
|
||||||
|
selectors: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execInstantiateComponet(ctx, vr, fn) {
|
||||||
|
|
||||||
|
if (vr.type != "VARIABLE") return error(ctx, fn, "Left hand instatiate component must be a variable");
|
||||||
|
if (fn.type != "FUNCTIONCALL") return error(ctx, fn, "Right type of instantiate component must be a function call");
|
||||||
|
|
||||||
|
const componentName = vr.name;
|
||||||
|
const templateName = fn.name;
|
||||||
|
|
||||||
|
const scopeLevel = getScopeLevel(ctx, templateName);
|
||||||
|
if (scopeLevel == -1) return error(ctx,fn, "Invalid Template");
|
||||||
|
const template = getScope(ctx, templateName);
|
||||||
|
|
||||||
|
if (template.type != "TEMPLATE") return error(ctx, fn, "Invalid Template");
|
||||||
|
|
||||||
|
|
||||||
|
const paramValues = [];
|
||||||
|
for (let i=0; i< fn.params.length; i++) {
|
||||||
|
const v = exec(ctx, fn.params[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (v.type != "NUMBER") return error(ctx, fn.params[i], "expected a number");
|
||||||
|
paramValues.push( v.value);
|
||||||
|
}
|
||||||
|
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
||||||
|
|
||||||
|
const vv = getScope(ctx, componentName, vr.selectors);
|
||||||
|
|
||||||
|
if (!vv) return error(ctx, vr, "Component not defined"+ componentName);
|
||||||
|
|
||||||
|
instantiateComponent(vv);
|
||||||
|
|
||||||
|
function instantiateComponent(varVal) {
|
||||||
|
if (Array.isArray(varVal)) {
|
||||||
|
for (let i =0; i<varVal.length; i++) {
|
||||||
|
instantiateComponent(varVal[i]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.components[varVal.fullName] != "UNINSTANTIATED") error(ctx, fn, "Component already instantiated");
|
||||||
|
|
||||||
|
const oldComponent = ctx.currentComponent;
|
||||||
|
const oldFileName = ctx.fileName;
|
||||||
|
const oldFilePath = ctx.filePath;
|
||||||
|
ctx.currentComponent = varVal.fullName;
|
||||||
|
|
||||||
|
ctx.components[ctx.currentComponent] = {
|
||||||
|
signals: [],
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const oldScopes = ctx.scopes;
|
||||||
|
|
||||||
|
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||||
|
|
||||||
|
const scope = {};
|
||||||
|
for (let i=0; i< template.params.length; i++) {
|
||||||
|
scope[template.params[i]] = {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: paramValues[i]
|
||||||
|
};
|
||||||
|
ctx.components[ctx.currentComponent].params[template.params[i]] = paramValues[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.components[ctx.currentComponent].template = templateName;
|
||||||
|
ctx.fileName = template.fileName;
|
||||||
|
ctx.filePath = template.filePath;
|
||||||
|
ctx.scopes = copyScope( template.scopes );
|
||||||
|
ctx.scopes.push(scope);
|
||||||
|
|
||||||
|
execBlock(ctx, template.block);
|
||||||
|
|
||||||
|
ctx.fileName = oldFileName;
|
||||||
|
ctx.filePath = oldFilePath;
|
||||||
|
ctx.currentComponent = oldComponent;
|
||||||
|
ctx.scopes = oldScopes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execFunctionCall(ctx, ast) {
|
||||||
|
|
||||||
|
const scopeLevel = getScopeLevel(ctx, ast.name);
|
||||||
|
if (scopeLevel == -1) return error(ctx, ast, "Function not defined: " + ast.name);
|
||||||
|
const fnc = getScope(ctx, ast.name);
|
||||||
|
|
||||||
|
if (fnc.type != "FUNCTION") return error(ctx, ast, "Not a function: " + ast.name);
|
||||||
|
|
||||||
|
const paramValues = [];
|
||||||
|
for (let i=0; i< ast.params.length; i++) {
|
||||||
|
const v = exec(ctx, ast.params[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (v.type != "NUMBER") return error(ctx, ast.params[i], "expected a number");
|
||||||
|
paramValues.push( v.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast.params.length != paramValues.length) error(ctx, ast, "Invalid Number of parameters");
|
||||||
|
|
||||||
|
const oldFileName = ctx.fileName;
|
||||||
|
const oldFilePath = ctx.filePath;
|
||||||
|
|
||||||
|
const oldScopes = ctx.scopes;
|
||||||
|
|
||||||
|
ctx.scopes = oldScopes.slice(0, scopeLevel+1);
|
||||||
|
|
||||||
|
const scope = {};
|
||||||
|
for (let i=0; i< fnc.params.length; i++) {
|
||||||
|
scope[fnc.params[i]] = {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: paramValues[i]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fileName = fnc.fileName;
|
||||||
|
ctx.filePath = fnc.filePath;
|
||||||
|
ctx.scopes = copyScope( fnc.scopes );
|
||||||
|
ctx.scopes.push(scope);
|
||||||
|
|
||||||
|
execBlock(ctx, fnc.block);
|
||||||
|
|
||||||
|
const res = ctx.returnValue;
|
||||||
|
ctx.returnValue = null;
|
||||||
|
|
||||||
|
ctx.fileName = oldFileName;
|
||||||
|
ctx.filePath = oldFilePath;
|
||||||
|
ctx.scopes = oldScopes;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execReturn(ctx, ast) {
|
||||||
|
ctx.returnValue = exec(ctx, ast.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execDeclareSignal(ctx, ast) {
|
||||||
|
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||||
|
|
||||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name");
|
||||||
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||||
|
|
||||||
|
const baseName = ctx.currentComponent ? ctx.currentComponent + "." + ast.name.name : ast.name.name;
|
||||||
|
|
||||||
|
const sizes=[];
|
||||||
|
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||||
|
const size = exec(ctx, ast.name.selectors[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||||
|
sizes.push( size.value.toJSNumber() );
|
||||||
|
}
|
||||||
|
|
||||||
|
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) {
|
||||||
|
ctx.signals[fullName] = {
|
||||||
|
fullName: fullName,
|
||||||
|
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
|
||||||
|
private: ast.private,
|
||||||
|
component: ctx.currentComponent,
|
||||||
|
equivalence: "",
|
||||||
|
alias: [fullName]
|
||||||
|
};
|
||||||
|
ctx.components[ctx.currentComponent].signals.push(fullName);
|
||||||
|
return {
|
||||||
|
type: "SIGNAL",
|
||||||
|
fullName: fullName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
type: "VARIABLE",
|
||||||
|
name: ast.name.name,
|
||||||
|
selectors: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execDeclareVariable(ctx, ast) {
|
||||||
|
const scope = ctx.scopes[ctx.scopes.length-1];
|
||||||
|
|
||||||
|
if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid linear combination name");
|
||||||
|
if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name);
|
||||||
|
|
||||||
|
const sizes=[];
|
||||||
|
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||||
|
const size = exec(ctx, ast.name.selectors[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (size.type != "NUMBER") return error(ctx, ast.name.selectors[i], "expected a number");
|
||||||
|
sizes.push( size.value.toJSNumber() );
|
||||||
|
}
|
||||||
|
|
||||||
|
scope[ast.name.name] = iterateSelectors(ctx, sizes, "", function() {
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: bigInt(0)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "VARIABLE",
|
||||||
|
name: ast.name.name,
|
||||||
|
selectors: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execVariable(ctx, ast) {
|
||||||
|
let v;
|
||||||
|
try {
|
||||||
|
v = getScope(ctx, ast.name, ast.selectors);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(JSON.stringify(ast, null,1));
|
||||||
|
}
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (!v) return error(ctx, ast, "Variable not defined");
|
||||||
|
let res;
|
||||||
|
res=v;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execPin(ctx, ast) {
|
||||||
|
const component = getScope(ctx, ast.component.name, ast.component.selectors);
|
||||||
|
if (!component) return error(ctx, ast.component, "Component does not exists: "+ast.component.name);
|
||||||
|
if (ctx.error) return;
|
||||||
|
let signalFullName = component.fullName + "." + ast.pin.name;
|
||||||
|
for (let i=0; i< ast.pin.selectors.length; i++) {
|
||||||
|
const sel = exec(ctx, ast.pin.selectors[i]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (sel.type != "NUMBER") return error(ctx, ast.pin.selectors[i], "expected a number");
|
||||||
|
|
||||||
|
signalFullName += "[" + sel.value.toJSNumber() + "]";
|
||||||
|
}
|
||||||
|
if (!ctx.signals[signalFullName]) error(ctx, ast, "Signal not defined:" + signalFullName);
|
||||||
|
return {
|
||||||
|
type: "SIGNAL",
|
||||||
|
fullName: signalFullName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execFor(ctx, ast) {
|
||||||
|
exec(ctx, ast.init);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
let v = exec(ctx, ast.condition);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||||
|
exec(ctx, ast.body);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
exec(ctx, ast.step);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
v = exec(ctx, ast.condition);
|
||||||
|
if (ctx.error) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execWhile(ctx, ast) {
|
||||||
|
let v = exec(ctx, ast.condition);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
while ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||||
|
exec(ctx, ast.body);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
v = exec(ctx, ast.condition);
|
||||||
|
if (ctx.error) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execIf(ctx, ast) {
|
||||||
|
let v = exec(ctx, ast.condition);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if ((v.value.neq(0))&&(!ctx.returnValue)) {
|
||||||
|
exec(ctx, ast.then);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else {
|
||||||
|
exec(ctx, ast.else);
|
||||||
|
if (ctx.error) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execVarAssignement(ctx, ast) {
|
||||||
|
let v;
|
||||||
|
if (ast.values[0].type == "DECLARE") {
|
||||||
|
v = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else {
|
||||||
|
v = ast.values[0];
|
||||||
|
}
|
||||||
|
const num = getScope(ctx, v.name, v.selectors);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if ((typeof(num) != "object")||(num == null)) return error(ctx, ast, "Variable not defined");
|
||||||
|
|
||||||
|
if (num.type == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1]);
|
||||||
|
|
||||||
|
const res = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
setScope(ctx, v.name, v.selectors, res);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execLt(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.lt(b.value) ? bigInt(1) : bigInt(0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execGt(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.gt(b.value) ? bigInt(1) : bigInt(0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execLte(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.lesserOrEquals(b.value) ? bigInt(1) : bigInt(0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execGte(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.greaterOrEquals(b.value) ? bigInt(1) : bigInt(0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execEq(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.eq(b.value) ? bigInt(1) : bigInt(0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execBAnd(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.and(b.value).and(__MASK__)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execShl(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.shiftLeft(v).and(__MASK__)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execShr(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
const v = b.value.greater(256) ? 256 : b.value.value;
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.shiftRight(v).and(__MASK__)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execMod(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.mod(b.value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execExp(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
|
return {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: a.value.modPow(b.value, __P__)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function execAdd(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
const res = lc.add(a,b);
|
||||||
|
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execSub(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
const res = lc.sub(a,b);
|
||||||
|
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execUMinus(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
const res = lc.negate(a);
|
||||||
|
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execMul(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
const res = lc.mul(a,b);
|
||||||
|
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function execVarAddAssignement(ctx, ast) {
|
||||||
|
const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||||
|
if (ctx.error) return;
|
||||||
|
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function execVarMulAssignement(ctx, ast) {
|
||||||
|
const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
|
||||||
|
if (ctx.error) return;
|
||||||
|
return execVarAssignement(ctx, { values: [ast.values[0], res] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function execPlusPlusRight(ctx, ast) {
|
||||||
|
const resBefore = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||||
|
if (ctx.error) return;
|
||||||
|
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||||
|
return resBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execPlusPlusLeft(ctx, ast) {
|
||||||
|
if (ctx.error) return;
|
||||||
|
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
|
||||||
|
if (ctx.error) return;
|
||||||
|
execVarAssignement(ctx, { values: [ast.values[0], resAfter] });
|
||||||
|
return resAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execTerCon(ctx, ast) {
|
||||||
|
const cond = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
if (!cond.value) return { type: "NUMBER" };
|
||||||
|
|
||||||
|
if (cond.value.neq(0)) {
|
||||||
|
return exec(ctx, ast.values[1]);
|
||||||
|
} else {
|
||||||
|
return exec(ctx, ast.values[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function execSignalAssign(ctx, ast) {
|
||||||
|
let vDest;
|
||||||
|
if (ast.values[0].type == "DECLARE") {
|
||||||
|
vDest = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else {
|
||||||
|
vDest = ast.values[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
let dst;
|
||||||
|
if (vDest.type == "VARIABLE") {
|
||||||
|
dst = getScope(ctx, vDest.name, vDest.selectors);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else if (vDest.type == "PIN") {
|
||||||
|
dst = execPin(ctx, vDest);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else {
|
||||||
|
error(ctx, ast, "Bad assignement");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dst) return error(ctx, ast, "Signal not defined");
|
||||||
|
if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
|
||||||
|
|
||||||
|
let sDest=ctx.signals[dst.fullName];
|
||||||
|
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName);
|
||||||
|
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||||
|
|
||||||
|
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
|
||||||
|
|
||||||
|
let src = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
let vSrc;
|
||||||
|
if (ast.values[1].type == "DECLARE") {
|
||||||
|
vSrc = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else {
|
||||||
|
vSrc = ast.values[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vSrc.type == "VARIABLE") {
|
||||||
|
src = getScope(ctx, vSrc.name, vSrc.selectors);
|
||||||
|
if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name);
|
||||||
|
if (ctx.error) return;
|
||||||
|
} else if (vSrc.type == "PIN") {
|
||||||
|
src = execPin(ctx, vSrc);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
let assignValue = true;
|
||||||
|
if (src.type == "SIGNAL") {
|
||||||
|
sDest.equivalence = src.fullName;
|
||||||
|
sDest.alias = sDest.alias.concat(src.alias);
|
||||||
|
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
|
||||||
|
assignValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assignValue) {
|
||||||
|
// const resLC = exec(ctx, vSrc);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
// const v = lc.evaluate(ctx, resLC);
|
||||||
|
const v = lc.evaluate(ctx, src);
|
||||||
|
|
||||||
|
if (v.value) {
|
||||||
|
sDest.value = v.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vDest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execConstrain(ctx, ast) {
|
||||||
|
const a = exec(ctx, ast.values[0]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
const b = exec(ctx, ast.values[1]);
|
||||||
|
if (ctx.error) return;
|
||||||
|
|
||||||
|
const res = lc.sub(a,b);
|
||||||
|
if (res.type == "ERROR") return error(ctx, ast, res.errStr);
|
||||||
|
|
||||||
|
if (!lc.isZero(res)) {
|
||||||
|
ctx.constraints.push(lc.toQEQ(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execSignalAssignConstrain(ctx, ast) {
|
||||||
|
const v = execSignalAssign(ctx,ast);
|
||||||
|
if (ctx.error) return;
|
||||||
|
execConstrain(ctx, ast);
|
||||||
|
if (ctx.error) return;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execInclude(ctx, ast) {
|
||||||
|
const incFileName = path.resolve(ctx.filePath, ast.file);
|
||||||
|
const incFilePath = path.dirname(incFileName);
|
||||||
|
|
||||||
|
ctx.includedFiles = ctx.includedFiles || [];
|
||||||
|
if (ctx.includedFiles[incFileName]) return;
|
||||||
|
|
||||||
|
ctx.includedFiles[incFileName] = true;
|
||||||
|
|
||||||
|
const src = fs.readFileSync(incFileName, "utf8");
|
||||||
|
|
||||||
|
if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
|
||||||
|
|
||||||
|
const incAst = parser.parse(src);
|
||||||
|
|
||||||
|
const oldFilePath = ctx.filePath;
|
||||||
|
const oldFileName = ctx.fileName;
|
||||||
|
ctx.filePath = incFilePath;
|
||||||
|
ctx.fileName = incFileName;
|
||||||
|
|
||||||
|
exec(ctx, incAst);
|
||||||
|
|
||||||
|
ast.block = incAst;
|
||||||
|
|
||||||
|
ctx.filePath = oldFilePath;
|
||||||
|
ctx.fileName = oldFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execArray(ctx, ast) {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
for (let i=0; i<ast.values.length; i++) {
|
||||||
|
res.push(exec(ctx, ast.values[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyScope(scope) {
|
||||||
|
var scopesClone = [];
|
||||||
|
for (let i=0; i<scope.length; i++) {
|
||||||
|
scopesClone.push(scope[i]);
|
||||||
|
}
|
||||||
|
return scopesClone;
|
||||||
|
}
|
||||||
|
|
||||||
1553
src/gencode.js
1553
src/gencode.js
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
|||||||
|
|
||||||
const assert = require("assert");
|
|
||||||
|
|
||||||
module.exports = iterateAST;
|
|
||||||
|
|
||||||
|
|
||||||
function iterateAST(ast, fn, _pfx) {
|
|
||||||
if (!ast) return;
|
|
||||||
|
|
||||||
const pfx = _pfx || "";
|
|
||||||
let itPfx = 0;
|
|
||||||
|
|
||||||
function getPfx() {
|
|
||||||
res = pfx+"."+itPfx;
|
|
||||||
itPfx ++;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let res = fn(ast, pfx);
|
|
||||||
if (res) return res;
|
|
||||||
function iterate(arr) {
|
|
||||||
if (arr) {
|
|
||||||
for (let i=0; i<arr.length; i++) {
|
|
||||||
res = iterateAST(arr[i], fn, getPfx());
|
|
||||||
if (res) return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ast.type == "NUMBER")) {
|
|
||||||
//
|
|
||||||
} else if (ast.type == "VARIABLE") {
|
|
||||||
iterate(ast.selectors);
|
|
||||||
} else if (ast.type == "PIN") {
|
|
||||||
iterate(ast.component.selectors);
|
|
||||||
iterate(ast.pin.selectors);
|
|
||||||
} else if (ast.type == "OP") {
|
|
||||||
iterate(ast.values);
|
|
||||||
} else if (ast.type == "DECLARE") {
|
|
||||||
iterate(ast.name.selectors);
|
|
||||||
} else if (ast.type == "FUNCTIONCALL") {
|
|
||||||
iterate(ast.params);
|
|
||||||
} else if (ast.type == "BLOCK") {
|
|
||||||
iterate(ast.statements);
|
|
||||||
} else if (ast.type == "COMPUTE") {
|
|
||||||
iterateAST(ast.body, fn, getPfx());
|
|
||||||
} else if (ast.type == "FOR") {
|
|
||||||
iterateAST(ast.init, fn, getPfx());
|
|
||||||
iterateAST(ast.condition, fn, getPfx());
|
|
||||||
iterateAST(ast.step, fn, getPfx());
|
|
||||||
iterateAST(ast.body, fn, getPfx());
|
|
||||||
} else if (ast.type == "WHILE") {
|
|
||||||
iterateAST(ast.condition, fn, getPfx());
|
|
||||||
iterateAST(ast.body, fn, getPfx());
|
|
||||||
} else if (ast.type == "IF") {
|
|
||||||
iterateAST(ast.condition, fn, getPfx());
|
|
||||||
iterateAST(ast.then, fn, getPfx());
|
|
||||||
iterateAST(ast.else, fn, getPfx());
|
|
||||||
} else if (ast.type == "RETURN") {
|
|
||||||
iterateAST(ast.value, fn, getPfx());
|
|
||||||
} else if (ast.type == "ARRAY") {
|
|
||||||
iterate(ast.values);
|
|
||||||
} else if ((ast.type == "TEMPLATEDEF")) {
|
|
||||||
//
|
|
||||||
} else if ((ast.type == "FUNCTIONDEF")) {
|
|
||||||
//
|
|
||||||
} else if ((ast.type == "INCLUDE")) {
|
|
||||||
//
|
|
||||||
} else {
|
|
||||||
assert(false, "GEN -> Invalid AST iteration: " + ast.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
760
src/lcalgebra.js
760
src/lcalgebra.js
@@ -1,572 +1,492 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 0KIMS association.
|
Copyright 2018 0KIMS association.
|
||||||
|
|
||||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
This file is part of jaz (Zero Knowledge Circuit Compiler).
|
||||||
|
|
||||||
circom is a free software: you can redistribute it and/or modify it
|
jaz is a free software: you can redistribute it and/or modify it
|
||||||
under the terms of the GNU General Public License as published by
|
under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
jaz is distributed in the hope that it will be useful, but WITHOUT
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
License for more details.
|
License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
along with jaz. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
|
|
||||||
// Number
|
NUMBER: a
|
||||||
///////////////
|
|
||||||
N: a
|
|
||||||
|
|
||||||
{
|
{
|
||||||
t: "N",
|
type: "NUMBER",
|
||||||
v: bigInt(a)
|
value: bigInt(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal
|
LINEARCOMBINATION: c1*s1 + c2*s2 + c3*s3
|
||||||
///////////////
|
|
||||||
{
|
|
||||||
t: "S",
|
|
||||||
sIdx: sIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linear Convination
|
|
||||||
//////////////////
|
|
||||||
LC: c1*s1 + c2*s2 + c3*s3
|
|
||||||
{
|
{
|
||||||
t: "LC",
|
type: "LINEARCOMBINATION",
|
||||||
coefs: {
|
values: {
|
||||||
s1: bigInt(c1),
|
s1: bigInt(c1),
|
||||||
s2: bigInt(c2),
|
s2: bigInt(c2),
|
||||||
s3: bigInt(c3)
|
s3: bigInt(c3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quadratic Expression
|
|
||||||
//////////////////
|
|
||||||
QEX: a*b + c WHERE a,b,c are LC
|
|
||||||
{
|
|
||||||
t: "QEX"
|
|
||||||
a: { t: "LC", coefs: {...} },
|
|
||||||
b: { t: "LC", coefs: {...} },
|
|
||||||
c: { t: "LC", coefs: {...} }
|
|
||||||
}
|
|
||||||
|
|
||||||
NQ: Non quadratic expression
|
QEQ: a*b + c WHERE a,b,c are LINEARCOMBINATION
|
||||||
{
|
{
|
||||||
t: "NQ"
|
type: "QEQ"
|
||||||
|
a: { type: LINEARCOMBINATION, values: {...} },
|
||||||
|
b: { type: LINEARCOMBINATION, values: {...} },
|
||||||
|
c: { type: LINEARCOMBINATION, values: {...} }
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
+ N LC QEX NQ
|
+ NUM LC QEQ
|
||||||
N N LC QEX NQ
|
NUM NUM LC QEQ
|
||||||
LC LC LC QEX NQ
|
LC LC LC QEQ
|
||||||
QEX QEX QEX NQ NQ
|
QEQ QEQ QEQ ERR
|
||||||
NQ NQ NQ NQ NQ
|
|
||||||
|
|
||||||
* N LC QEX NQ
|
* NUM LC QEQ
|
||||||
N N LC QEX NQ
|
NUM NUM LC QEQ
|
||||||
LC LC QEX NQ NQ
|
LC LC QEQ ERR
|
||||||
QEX QEX NQ NQ NQ
|
QEQ QEQ ERR ERR
|
||||||
NQ NQ NQ NQ NQ
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const utils = require("./utils");
|
const bigInt = require("big-integer");
|
||||||
const sONE = 0;
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
|
|
||||||
class LCAlgebra {
|
exports.add = add;
|
||||||
constructor (aField) {
|
exports.mul = mul;
|
||||||
const self = this;
|
exports.evaluate = evaluate;
|
||||||
this.F= aField;
|
exports.negate = negate;
|
||||||
[
|
exports.sub = sub;
|
||||||
["idiv",2],
|
exports.toQEQ = toQEQ;
|
||||||
["mod",2],
|
exports.isZero = isZero;
|
||||||
["band",2],
|
exports.toString = toString;
|
||||||
["bor",2],
|
exports.canonize = canonize;
|
||||||
["bxor",2],
|
exports.substitute = substitute;
|
||||||
["bnot",2],
|
|
||||||
["land",2],
|
|
||||||
["lor",2],
|
|
||||||
["lnot",2],
|
|
||||||
["shl",2],
|
|
||||||
["shr",2],
|
|
||||||
["lt",2, true],
|
|
||||||
["leq",2, true],
|
|
||||||
["eq",2, true],
|
|
||||||
["neq",2, true],
|
|
||||||
["geq",2, true],
|
|
||||||
["gt",2, true]
|
|
||||||
].forEach( (op) => {
|
|
||||||
self._genNQOp(op[0], op[1], op[2]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_genNQOp(op, nOps, adjustBool) {
|
function signal2lc(a) {
|
||||||
const self=this;
|
let lc;
|
||||||
self[op] = function() {
|
if (a.type == "SIGNAL") {
|
||||||
const operands = [];
|
lc = {
|
||||||
for (let i=0; i<nOps; i++) {
|
type: "LINEARCOMBINATION",
|
||||||
if (typeof(arguments[i]) !== "object") throw new Error("Invalid operand type");
|
values: {}
|
||||||
if (arguments[i].t !== "N") return {t: "NQ"};
|
|
||||||
operands.push(arguments[i].v);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
t: "N",
|
|
||||||
v: adjustBool ? ( self.F[op](...operands) ? self.F.one: self.F.zero) : self.F[op](...operands)
|
|
||||||
};
|
};
|
||||||
};
|
lc.values[a.fullName] = bigInt(1);
|
||||||
}
|
|
||||||
|
|
||||||
_signal2lc(a) {
|
|
||||||
const self = this;
|
|
||||||
if (a.t == "S") {
|
|
||||||
const lc = {
|
|
||||||
t: "LC",
|
|
||||||
coefs: {}
|
|
||||||
};
|
|
||||||
lc.coefs[a.sIdx] = self.F.one;
|
|
||||||
return lc;
|
return lc;
|
||||||
} else {
|
} else {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clone(a) {
|
||||||
_clone(a) {
|
|
||||||
const res = {};
|
const res = {};
|
||||||
res.t = a.t;
|
res.type = a.type;
|
||||||
if (a.t == "N") {
|
if (a.type == "NUMBER") {
|
||||||
res.v = a.v;
|
res.value = bigInt(a.value);
|
||||||
} else if (a.t == "S") {
|
} else if (a.type == "LINEARCOMBINATION") {
|
||||||
res.sIdx = a.sIdx;
|
res.values = {};
|
||||||
} else if (a.t == "LC") {
|
for (let k in a.values) {
|
||||||
res.coefs = {};
|
res.values[k] = bigInt(a.values[k]);
|
||||||
for (let k in a.coefs) {
|
|
||||||
res.coefs[k] = a.coefs[k];
|
|
||||||
}
|
}
|
||||||
} else if (a.t == "QEX") {
|
} else if (a.type == "QEQ") {
|
||||||
res.a = this._clone(a.a);
|
res.a = clone(a.a);
|
||||||
res.b = this._clone(a.b);
|
res.b = clone(a.b);
|
||||||
res.c = this._clone(a.c);
|
res.c = clone(a.c);
|
||||||
|
} else if (a.type == "ERROR") {
|
||||||
|
res.errStr = a.errStr;
|
||||||
|
} else {
|
||||||
|
res.type = "ERROR";
|
||||||
|
res.errStr = "Invilid type when clonning: "+a.type;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(_a,_b) {
|
function add(_a, _b) {
|
||||||
const self = this;
|
const a = signal2lc(_a);
|
||||||
const a = self._signal2lc(_a);
|
const b = signal2lc(_b);
|
||||||
const b = self._signal2lc(_b);
|
if (a.type == "ERROR") return a;
|
||||||
if (a.t == "NQ") return a;
|
if (b.type == "ERROR") return b;
|
||||||
if (b.t == "NQ") return b;
|
if (a.type == "NUMBER") {
|
||||||
if (a.t == "N") {
|
if (b.type == "NUMBER") {
|
||||||
if (b.t == "N") {
|
return addNumNum(a,b);
|
||||||
return add_N_N(a,b);
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
} else if (b.t=="LC") {
|
return addLCNum(b,a);
|
||||||
return add_LC_N(b,a);
|
} else if (b.type=="QEQ") {
|
||||||
} else if (b.t=="QEX") {
|
return addQEQNum(b,a);
|
||||||
return add_QEX_N(b,a);
|
|
||||||
} else {
|
} else {
|
||||||
return { type: "NQ" };
|
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||||
}
|
}
|
||||||
} else if (a.t=="LC") {
|
} else if (a.type=="LINEARCOMBINATION") {
|
||||||
if (b.t == "N") {
|
if (b.type == "NUMBER") {
|
||||||
return add_LC_N(a,b);
|
return addLCNum(a,b);
|
||||||
} else if (b.t=="LC") {
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
return add_LC_LC(a,b);
|
return addLCLC(a,b);
|
||||||
} else if (b.t=="QEX") {
|
} else if (b.type=="QEQ") {
|
||||||
return add_QEX_LC(b,a);
|
return addQEQLC(b,a);
|
||||||
} else {
|
} else {
|
||||||
return { t: "NQ" };
|
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||||
}
|
}
|
||||||
} else if (a.t=="QEX") {
|
} else if (a.type=="QEQ") {
|
||||||
if (b.t == "N") {
|
if (b.type == "NUMBER") {
|
||||||
return add_QEX_N(a,b);
|
return addQEQNum(a,b);
|
||||||
} else if (b.t=="LC") {
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
return add_QEX_LC(a,b);
|
return addQEQLC(a,b);
|
||||||
} else if (b.t=="QEX") {
|
} else if (b.type=="QEQ") {
|
||||||
return { t: "NQ" };
|
return { type: "ERROR", errStr: "QEQ + QEQ" };
|
||||||
} else {
|
} else {
|
||||||
return { t: "NQ" };
|
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return { t: "NQ" };
|
return { type: "ERROR", errStr: "LC Add Invalid Type 1: "+a.type };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_N_N(a,b) {
|
function addNumNum(a,b) {
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
return {
|
return {
|
||||||
t: "N",
|
type: "NUMBER",
|
||||||
v: self.F.add(a.v, b.v)
|
value: a.value.add(b.value).mod(__P__)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_LC_N(a,b) {
|
function addLCNum(a,b) {
|
||||||
let res = self._clone(a);
|
let res = clone(a);
|
||||||
if (self.F.isZero(b.v)) return res;
|
if (!b.value) {
|
||||||
if (!utils.isDefined(res.coefs[sONE])) {
|
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
||||||
res.coefs[sONE]= b.v;
|
}
|
||||||
|
if (b.value.isZero()) return res;
|
||||||
|
if (!res.values["one"]) {
|
||||||
|
res.values["one"]=bigInt(b.value);
|
||||||
} else {
|
} else {
|
||||||
res.coefs[sONE]= self.F.add(res.coefs[sONE], b.v);
|
res.values["one"]= res.values["one"].add(b.value).mod(__P__);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_LC_LC(a,b) {
|
function addLCLC(a,b) {
|
||||||
let res = self._clone(a);
|
let res = clone(a);
|
||||||
for (let k in b.coefs) {
|
for (let k in b.values) {
|
||||||
if (!utils.isDefined(res.coefs[k])) {
|
if (!res.values[k]) {
|
||||||
res.coefs[k]=b.coefs[k];
|
res.values[k]=bigInt(b.values[k]);
|
||||||
} else {
|
} else {
|
||||||
res.coefs[k]= self.F.add(res.coefs[k], b.coefs[k]);
|
res.values[k]= res.values[k].add(b.values[k]).mod(__P__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_QEX_N(a,b) {
|
function addQEQNum(a,b) {
|
||||||
let res = self._clone(a);
|
let res = clone(a);
|
||||||
res.c = add_LC_N(res.c, b);
|
res.c = addLCNum(res.c, b);
|
||||||
|
if (res.c.type == "ERROR") return res.c;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_QEX_LC(a,b) {
|
function addQEQLC(a,b) {
|
||||||
let res = self._clone(a);
|
let res = clone(a);
|
||||||
res.c = add_LC_LC(res.c, b);
|
res.c = addLCLC(res.c, b);
|
||||||
|
if (res.c.type == "ERROR") return res.c;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mul(_a, _b) {
|
||||||
|
const a = signal2lc(_a);
|
||||||
|
const b = signal2lc(_b);
|
||||||
|
if (a.type == "ERROR") return a;
|
||||||
|
if (b.type == "ERROR") return b;
|
||||||
|
if (a.type == "NUMBER") {
|
||||||
|
if (b.type == "NUMBER") {
|
||||||
|
return mulNumNum(a,b);
|
||||||
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
|
return mulLCNum(b,a);
|
||||||
|
} else if (b.type=="QEQ") {
|
||||||
|
return mulQEQNum(b,a);
|
||||||
|
} else {
|
||||||
|
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||||
|
}
|
||||||
|
} else if (a.type=="LINEARCOMBINATION") {
|
||||||
|
if (b.type == "NUMBER") {
|
||||||
|
return mulLCNum(a,b);
|
||||||
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
|
return mulLCLC(a,b);
|
||||||
|
} else if (b.type=="QEQ") {
|
||||||
|
return { type: "ERROR", errStr: "LC * QEQ" };
|
||||||
|
} else {
|
||||||
|
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||||
|
}
|
||||||
|
} else if (a.type=="QEQ") {
|
||||||
|
if (b.type == "NUMBER") {
|
||||||
|
return mulQEQNum(a,b);
|
||||||
|
} else if (b.type=="LINEARCOMBINATION") {
|
||||||
|
return { type: "ERROR", errStr: "QEC * LC" };
|
||||||
|
} else if (b.type=="QEQ") {
|
||||||
|
return { type: "ERROR", errStr: "QEQ * QEQ" };
|
||||||
|
} else {
|
||||||
|
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { type: "ERROR", errStr: "LC Mul Invalid Type 1: "+a.type };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mul(_a,_b) {
|
|
||||||
const self = this;
|
|
||||||
const a = self._signal2lc(_a);
|
|
||||||
const b = self._signal2lc(_b);
|
|
||||||
if (a.t == "NQ") return a;
|
|
||||||
if (b.t == "NQ") return b;
|
|
||||||
if (a.t == "N") {
|
|
||||||
if (b.t == "N") {
|
|
||||||
return mul_N_N(a,b);
|
|
||||||
} else if (b.t=="LC") {
|
|
||||||
return mul_LC_N(b,a);
|
|
||||||
} else if (b.t=="QEX") {
|
|
||||||
return mul_QEX_N(b,a);
|
|
||||||
} else {
|
|
||||||
return { t: "NQ"};
|
|
||||||
}
|
|
||||||
} else if (a.t=="LC") {
|
|
||||||
if (b.t == "N") {
|
|
||||||
return mul_LC_N(a,b);
|
|
||||||
} else if (b.t=="LC") {
|
|
||||||
return mul_LC_LC(a,b);
|
|
||||||
} else if (b.t=="QEX") {
|
|
||||||
return { t: "NQ" };
|
|
||||||
} else {
|
|
||||||
return { t: "NQ" };
|
|
||||||
}
|
|
||||||
} else if (a.t=="QEX") {
|
|
||||||
if (b.t == "N") {
|
|
||||||
return mul_QEX_N(a,b);
|
|
||||||
} else if (b.t=="LC") {
|
|
||||||
return { t: "NQ" };
|
|
||||||
} else if (b.t=="QEX") {
|
|
||||||
return { t: "NQ" };
|
|
||||||
} else {
|
|
||||||
return { t: "NQ" };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return { t: "NQ" };
|
|
||||||
}
|
|
||||||
|
|
||||||
function mul_N_N(a,b) {
|
function mulNumNum(a,b) {
|
||||||
|
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||||
return {
|
return {
|
||||||
t: "N",
|
type: "NUMBER",
|
||||||
v: self.F.mul(a.v, b.v)
|
value: a.value.times(b.value).mod(__P__)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mul_LC_N(a,b) {
|
function mulLCNum(a,b) {
|
||||||
let res = self._clone(a);
|
let res = clone(a);
|
||||||
for (let k in res.coefs) {
|
if (!b.value) {
|
||||||
res.coefs[k] = self.F.mul(res.coefs[k], b.v);
|
return {type: "ERROR", errStr: "LinearCombination * undefined"};
|
||||||
|
}
|
||||||
|
for (let k in res.values) {
|
||||||
|
res.values[k] = res.values[k].times(b.value).mod(__P__);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mul_LC_LC(a,b) {
|
function mulLCLC(a,b) {
|
||||||
return {
|
return {
|
||||||
t: "QEX",
|
type: "QEQ",
|
||||||
a: self._clone(a),
|
a: clone(a),
|
||||||
b: self._clone(b),
|
b: clone(b),
|
||||||
c: { t: "LC", coefs: {}}
|
c: { type: "LINEARCOMBINATION", values: {}}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mul_QEX_N(a,b) {
|
function mulQEQNum(a,b) {
|
||||||
return {
|
let res = {
|
||||||
t: "QEX",
|
type: "QEQ",
|
||||||
a: mul_LC_N(a.a, b),
|
a: mulLCNum(a.a, b),
|
||||||
b: self._clone(a.b),
|
b: clone(a.b),
|
||||||
c: mul_LC_N(a.c, b)
|
c: mulLCNum(a.c, b)
|
||||||
};
|
};
|
||||||
}
|
if (res.a.type == "ERROR") return res.a;
|
||||||
}
|
if (res.c.type == "ERROR") return res.a;
|
||||||
|
|
||||||
neg(_a) {
|
|
||||||
const a = this._signal2lc(_a);
|
|
||||||
let res = this._clone(a);
|
|
||||||
if (res.t == "N") {
|
|
||||||
res.v = this.F.neg(a.v);
|
|
||||||
} else if (res.t == "LC") {
|
|
||||||
for (let k in res.coefs) {
|
|
||||||
res.coefs[k] = this.F.neg(res.coefs[k]);
|
|
||||||
}
|
|
||||||
} else if (res.t == "QEX") {
|
|
||||||
res.a = this.neg(res.a);
|
|
||||||
res.c = this.neg(res.c);
|
|
||||||
} else {
|
|
||||||
res = {t: "NQ"};
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub(a, b) {
|
function getSignalValue(ctx, signalName) {
|
||||||
return this.add(a, this.neg(b));
|
const s = ctx.signals[signalName];
|
||||||
}
|
if (s.equivalence != "") {
|
||||||
|
return getSignalValue(ctx, s.equivalence);
|
||||||
div(a, b) {
|
|
||||||
if (b.t == "N") {
|
|
||||||
if (this.F.isZero(b.v)) throw new Error("Division by zero");
|
|
||||||
const inv = {
|
|
||||||
t: "N",
|
|
||||||
v: this.F.inv(b.v)
|
|
||||||
};
|
|
||||||
return this.mul(a, inv);
|
|
||||||
} else {
|
} else {
|
||||||
return {t: "NQ"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pow(a, b) {
|
|
||||||
if (b.t == "N") {
|
|
||||||
if (this.F.isZero(b.v)) {
|
|
||||||
if (this.isZero(a)) {
|
|
||||||
throw new Error("Zero to the Zero");
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
t: "N",
|
|
||||||
v: this.F.one
|
|
||||||
};
|
|
||||||
} else if (this.F.eq(b.v, this.F.one)) {
|
|
||||||
return a;
|
|
||||||
} else if (this.F.eq(b.v, this.F.two)) {
|
|
||||||
return this.mul(a,a);
|
|
||||||
} else {
|
|
||||||
if (a.t=="N") {
|
|
||||||
return {
|
|
||||||
t: "N",
|
|
||||||
v: this.F.pow(a.v, b.v)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {t: "NQ"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {t: "NQ"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
substitute(where, signal, equivalence) {
|
|
||||||
if (equivalence.t != "LC") throw new Error("Equivalence must be a Linear Combination");
|
|
||||||
if (where.t == "LC") {
|
|
||||||
if (!utils.isDefined(where.coefs[signal]) || this.F.isZero(where.coefs[signal])) return where;
|
|
||||||
const res=this._clone(where);
|
|
||||||
const coef = res.coefs[signal];
|
|
||||||
for (let k in equivalence.coefs) {
|
|
||||||
if (k != signal) {
|
|
||||||
const v = this.F.mul( coef, equivalence.coefs[k] );
|
|
||||||
if (!utils.isDefined(res.coefs[k])) {
|
|
||||||
res.coefs[k]=v;
|
|
||||||
} else {
|
|
||||||
res.coefs[k]= this.F.add(res.coefs[k],v);
|
|
||||||
}
|
|
||||||
if (this.F.isZero(res.coefs[k])) delete res.coefs[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete res.coefs[signal];
|
|
||||||
return res;
|
|
||||||
} else if (where.t == "QEX") {
|
|
||||||
const res = {
|
const res = {
|
||||||
t: "QEX",
|
type: "NUMBER"
|
||||||
a: this.substitute(where.a, signal, equivalence),
|
|
||||||
b: this.substitute(where.b, signal, equivalence),
|
|
||||||
c: this.substitute(where.c, signal, equivalence)
|
|
||||||
};
|
};
|
||||||
|
if (s.value) {
|
||||||
|
res.value = s.value;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
} else {
|
|
||||||
return where;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toQEX(a) {
|
function evaluate(ctx, n) {
|
||||||
if (a.t == "N") {
|
if (n.type == "NUMBER") {
|
||||||
const res = {
|
return n;
|
||||||
t: "QEX",
|
} else if (n.type == "SIGNAL") {
|
||||||
a: {t: "LC", coefs: {}},
|
return getSignalValue(ctx, n.fullName);
|
||||||
b: {t: "LC", coefs: {}},
|
} else if (n.type == "LINEARCOMBINATION") {
|
||||||
c: {t: "LC", coefs: {}}
|
const v= {
|
||||||
|
type: "NUMBER",
|
||||||
|
value: bigInt(0)
|
||||||
};
|
};
|
||||||
res.c[sONE] = a.v;
|
for (let k in n.values) {
|
||||||
return res;
|
const s = getSignalValue(ctx, k);
|
||||||
} else if (a.t == "LC") {
|
if (s.type != "NUMBER") return {type: "ERROR", errStr: "Invalid signal in linear Combination: " + k};
|
||||||
|
if (!s.value) return { type: "NUMBER" };
|
||||||
|
v.value = v.value.add( n.values[k].times(s.value)).mod(__P__);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
} else if (n.type == "QEQ") {
|
||||||
|
const a = evaluate(ctx, n.a);
|
||||||
|
if (a.type == "ERROR") return a;
|
||||||
|
if (!a.value) return { type: "NUMBER" };
|
||||||
|
const b = evaluate(ctx, n.b);
|
||||||
|
if (b.type == "ERROR") return b;
|
||||||
|
if (!b.value) return { type: "NUMBER" };
|
||||||
|
const c = evaluate(ctx, n.c);
|
||||||
|
if (c.type == "ERROR") return c;
|
||||||
|
if (!c.value) return { type: "NUMBER" };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
t: "QEX",
|
type: "NUMBER",
|
||||||
a: {t: "LC", coefs: {}},
|
value: (a.value.times(b.value).add(c.value)).mod(__P__)
|
||||||
b: {t: "LC", coefs: {}},
|
|
||||||
c: this._clone(a)
|
|
||||||
};
|
};
|
||||||
} else if (a.t == "QEX") {
|
} else if (n.type == "ERROR") {
|
||||||
return this._clone(a);
|
return n;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Type ${a.t} can not be converted to QEX`);
|
return {type: "ERROR", errStr: "Invalid type in evaluate: "+n.type};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isZero(a) {
|
function negate(_a) {
|
||||||
if (a.t == "N") {
|
const a = signal2lc(_a);
|
||||||
return this.F.isZero(a.v);
|
let res = clone(a);
|
||||||
} else if (a.t == "LC") {
|
if (res.type == "NUMBER") {
|
||||||
for (let k in a.coefs) {
|
res.value = __P__.minus(a.value).mod(__P__);
|
||||||
if (!this.F.isZero(a.coefs[k])) return false;
|
} else if (res.type == "LINEARCOMBINATION") {
|
||||||
|
for (let k in res.values) {
|
||||||
|
res.values[k] = __P__.minus(res.values[k]).mod(__P__);
|
||||||
|
}
|
||||||
|
} else if (res.type == "QEQ") {
|
||||||
|
res.a = negate(res.a);
|
||||||
|
res.c = negate(res.c);
|
||||||
|
} else if (res.type == "ERROR") {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
res = {type: "ERROR", errStr: "LC Negate invalid Type: "+res.type};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sub(a, b) {
|
||||||
|
return add(a, negate(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toQEQ(a) {
|
||||||
|
if (a.type == "NUMBER") {
|
||||||
|
return {
|
||||||
|
type: "QEQ",
|
||||||
|
a: {type: "LINEARCOMBINATION", values: {}},
|
||||||
|
b: {type: "LINEARCOMBINATION", values: {}},
|
||||||
|
c: {type: "LINEARCOMBINATION", values: {"one": bigInt(a.value)}}
|
||||||
|
};
|
||||||
|
} else if (a.type == "LINEARCOMBINATION") {
|
||||||
|
return {
|
||||||
|
type: "QEQ",
|
||||||
|
a: {type: "LINEARCOMBINATION", values: {}},
|
||||||
|
b: {type: "LINEARCOMBINATION", values: {}},
|
||||||
|
c: clone(a)
|
||||||
|
};
|
||||||
|
} else if (a.type == "QEQ") {
|
||||||
|
return clone(a);
|
||||||
|
} else if (a.type == "ERROR") {
|
||||||
|
return clone(a);
|
||||||
|
} else {
|
||||||
|
return {type: "ERROR", errStr: "toQEQ invalid Type: "+a.type};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isZero(a) {
|
||||||
|
if (a.type == "NUMBER") {
|
||||||
|
return a.value.isZero();
|
||||||
|
} else if (a.type == "LINEARCOMBINATION") {
|
||||||
|
for (let k in a.values) {
|
||||||
|
if (!a.values[k].isZero()) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (a.t == "QEX") {
|
} else if (a.type == "QEQ") {
|
||||||
return (this.isZero(a.a) || this.isZero(a.b)) && this.isZero(a.c);
|
return (isZero(a.a) || isZero(a.b)) && isZero(a.c);
|
||||||
|
} else if (a.type == "ERROR") {
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(a, ctx) {
|
function toString(a, ctx) {
|
||||||
if (a.t == "N") {
|
if (a.type == "NUMBER") {
|
||||||
return a.v.toString();
|
return a.value.toString();
|
||||||
} else if (a.t == "LC") {
|
} else if (a.type == "LINEARCOMBINATION") {
|
||||||
let S="";
|
let S="";
|
||||||
for (let k in a.coefs) {
|
for (let k in a.values) {
|
||||||
if (!this.F.isZero(a.coefs[k])) {
|
if (!a.values[k].isZero()) {
|
||||||
let c;
|
let c;
|
||||||
if (a.coefs[k].greater(this.F.p.divide(2))) {
|
if (a.values[k].greater(__P__.divide(2))) {
|
||||||
S = S + "-";
|
S = S + "-";
|
||||||
c = this.F.p.minus(a.coefs[k]);
|
c = __P__.minus(a.values[k]);
|
||||||
} else {
|
} else {
|
||||||
if (S!="") S=S+" + ";
|
if (S!="") S=S+" + ";
|
||||||
c = a.coefs[k];
|
c = a.values[k];
|
||||||
}
|
}
|
||||||
if (!c.equals(this.F.one)) {
|
if (!c.equals(1)) {
|
||||||
S = S + c.toString() + "*";
|
S = S + c.toString() + "*";
|
||||||
}
|
}
|
||||||
let sIdx = k;
|
let sigName = k;
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
|
while (ctx.signals[sigName].equivalence) sigName = ctx.signals[sigName].equivalence;
|
||||||
}
|
}
|
||||||
S = S + "[" + sIdx + "]";
|
S = S + sigName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (S=="") return "0"; else return S;
|
if (S=="") return "0"; else return S;
|
||||||
} else if (a.t == "QEX") {
|
} else if (a.type == "QEQ") {
|
||||||
return "( "+
|
return "( "+toString(a.a, ctx)+" ) * ( "+toString(a.b, ctx)+" ) + " + toString(a.c, ctx);
|
||||||
this.toString(a.a, ctx)+" ) * ( "+
|
} else if (a.type == "ERROR") {
|
||||||
this.toString(a.b, ctx)+" ) + " +
|
return "ERROR: "+a.errStr;
|
||||||
this.toString(a.c, ctx);
|
|
||||||
} else {
|
} else {
|
||||||
return "NQ";
|
return "INVALID";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate(ctx, n) {
|
function canonize(ctx, a) {
|
||||||
if (n.t == "N") {
|
if (a.type == "LINEARCOMBINATION") {
|
||||||
return n.v;
|
for (let k in a.values) {
|
||||||
} else if (n.t == "SIGNAL") {
|
|
||||||
return getSignalValue(ctx, n.sIdx);
|
|
||||||
} else if (n.t == "LC") {
|
|
||||||
let v= this.F.zero;
|
|
||||||
for (let k in n.coefs) {
|
|
||||||
const s = getSignalValue(ctx, k);
|
|
||||||
if (s === null) return null;
|
|
||||||
v = this.F.add(v, this.F.mul( n.coefs[k], s));
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
} else if (n.type == "QEX") {
|
|
||||||
const a = this.evaluate(ctx, n.a);
|
|
||||||
if (a === null) return null;
|
|
||||||
const b = this.evaluate(ctx, n.b);
|
|
||||||
if (b === null) return null;
|
|
||||||
const c = this.evaluate(ctx, n.c);
|
|
||||||
if (c === null) return null;
|
|
||||||
|
|
||||||
return this.F.add(this.F.mul(a,b), c);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getSignalValue(ctx, sIdx) {
|
|
||||||
let s = ctx.signals[sIdx];
|
|
||||||
while (s.e>=0) s = ctx.signals[s.e];
|
|
||||||
if (utils.isDefined(s.v)) return s.v;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
canonize(ctx, a) {
|
|
||||||
if (a.t == "LC") {
|
|
||||||
const res = this._clone(a);
|
|
||||||
for (let k in a.coefs) {
|
|
||||||
let s = k;
|
let s = k;
|
||||||
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
|
while (ctx.signals[s].equivalence) s= ctx.signals[s].equivalence;
|
||||||
if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
|
if ((typeof(ctx.signals[s].value) != "undefined")&&(k != "one")) {
|
||||||
const v = this.F.mul(res.coefs[k], ctx.signals[s].v);
|
const v = a.values[k].times(ctx.signals[s].value).mod(__P__);
|
||||||
if (!utils.isDefined(res.coefs[sONE])) {
|
if (!a.values["one"]) {
|
||||||
res.coefs[sONE]=v;
|
a.values["one"]=v;
|
||||||
} else {
|
} else {
|
||||||
res.coefs[sONE]= this.F.add(res.coefs[sONE], v);
|
a.values["one"]= a.values["one"].add(v).mod(__P__);
|
||||||
}
|
}
|
||||||
delete res.coefs[k];
|
delete a.values[k];
|
||||||
} else if (s != k) {
|
} else if (s != k) {
|
||||||
if (!utils.isDefined(res.coefs[s])) {
|
if (!a.values[s]) {
|
||||||
res.coefs[s]=res.coefs[k];
|
a.values[s]=bigInt(a.values[k]);
|
||||||
} else {
|
} else {
|
||||||
res.coefs[s]= this.F.add(res.coefs[s], res.coefs[k]);
|
a.values[s]= a.values[s].add(a.values[k]).mod(__P__);
|
||||||
}
|
}
|
||||||
delete res.coefs[k];
|
delete a.values[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let k in res.coefs) {
|
for (let k in a.values) {
|
||||||
if (this.F.isZero(res.coefs[k])) delete res.coefs[k];
|
if (a.values[k].isZero()) delete a.values[k];
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
} else if (a.type == "QEQ") {
|
||||||
|
a.a = canonize(ctx, a.a);
|
||||||
|
a.b = canonize(ctx, a.b);
|
||||||
|
a.c = canonize(ctx, a.c);
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
} else if (a.t == "QEX") {
|
|
||||||
const res = {
|
|
||||||
t: "QEX",
|
|
||||||
a: this.canonize(ctx, a.a),
|
|
||||||
b: this.canonize(ctx, a.b),
|
|
||||||
c: this.canonize(ctx, a.c)
|
|
||||||
};
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function substitute(where, signal, equivalence) {
|
||||||
|
if (equivalence.type != "LINEARCOMBINATION") throw new Error("Equivalence must be a Linear Combination");
|
||||||
|
if (where.type == "LINEARCOMBINATION") {
|
||||||
|
if (!where.values[signal] || where.values[signal].isZero()) return where;
|
||||||
|
const coef = where.values[signal];
|
||||||
|
for (let k in equivalence.values) {
|
||||||
|
if (k != signal) {
|
||||||
|
const v = coef.times(equivalence.values[k]).mod(__P__);
|
||||||
|
if (!where.values[k]) {
|
||||||
|
where.values[k]=v;
|
||||||
|
} else {
|
||||||
|
where.values[k]= where.values[k].add(v).mod(__P__);
|
||||||
|
}
|
||||||
|
if (where.values[k].isZero()) delete where.values[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete where.values[signal];
|
||||||
|
} else if (where.type == "QEQ") {
|
||||||
|
substitute(where.a, signal, equivalence);
|
||||||
|
substitute(where.b, signal, equivalence);
|
||||||
|
substitute(where.c, signal, equivalence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = LCAlgebra;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
146
src/r1csfile.js
146
src/r1csfile.js
@@ -1,146 +0,0 @@
|
|||||||
|
|
||||||
const fastFile = require("fastfile");
|
|
||||||
const assert = require("assert");
|
|
||||||
const BigArray = require("./bigarray");
|
|
||||||
|
|
||||||
module.exports.buildR1cs = buildR1cs;
|
|
||||||
|
|
||||||
|
|
||||||
async function buildR1cs(ctx, fileName) {
|
|
||||||
|
|
||||||
const fd = await fastFile.createOverride(fileName, 1<<22, 1<<24);
|
|
||||||
|
|
||||||
const buffBigInt = new Uint8Array(ctx.F.n8);
|
|
||||||
|
|
||||||
const type = "r1cs";
|
|
||||||
const buff = new Uint8Array(4);
|
|
||||||
for (let i=0; i<4; i++) buff[i] = type.charCodeAt(i);
|
|
||||||
await fd.write(buff, 0); // Magic "r1cs"
|
|
||||||
|
|
||||||
await fd.writeULE32(1); // Version
|
|
||||||
await fd.writeULE32(3); // Number of Sections
|
|
||||||
|
|
||||||
// Write the header
|
|
||||||
///////////
|
|
||||||
await fd.writeULE32(1); // Header type
|
|
||||||
const pHeaderSize = fd.pos;
|
|
||||||
await fd.writeULE64(0); // Temporally set to 0 length
|
|
||||||
|
|
||||||
|
|
||||||
const n8 = (Math.floor( (ctx.F.bitLength - 1) / 64) +1)*8;
|
|
||||||
// Field Def
|
|
||||||
await fd.writeULE32(n8); // Temporally set to 0 length
|
|
||||||
await writeBigInt(ctx.F.p);
|
|
||||||
|
|
||||||
const NWires =
|
|
||||||
ctx.totals[ctx.stONE] +
|
|
||||||
ctx.totals[ctx.stOUTPUT] +
|
|
||||||
ctx.totals[ctx.stPUBINPUT] +
|
|
||||||
ctx.totals[ctx.stPRVINPUT] +
|
|
||||||
ctx.totals[ctx.stINTERNAL];
|
|
||||||
|
|
||||||
await fd.writeULE32(NWires);
|
|
||||||
await fd.writeULE32(ctx.totals[ctx.stOUTPUT]);
|
|
||||||
await fd.writeULE32(ctx.totals[ctx.stPUBINPUT]);
|
|
||||||
await fd.writeULE32(ctx.totals[ctx.stPRVINPUT]);
|
|
||||||
await fd.writeULE64(ctx.signals.length);
|
|
||||||
await fd.writeULE32(ctx.constraints.length);
|
|
||||||
|
|
||||||
const headerSize = fd.pos - pHeaderSize - 8;
|
|
||||||
|
|
||||||
// Write constraints
|
|
||||||
///////////
|
|
||||||
await fd.writeULE32(2); // Constraints type
|
|
||||||
const pConstraintsSize = fd.pos;
|
|
||||||
await fd.writeULE64(0); // Temporally set to 0 length
|
|
||||||
|
|
||||||
for (let i=0; i<ctx.constraints.length; i++) {
|
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
|
||||||
if (ctx.verbose) console.log("writing constraint: ", i);
|
|
||||||
}
|
|
||||||
await writeConstraint(ctx.constraints[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const constraintsSize = fd.pos - pConstraintsSize - 8;
|
|
||||||
|
|
||||||
// Write map
|
|
||||||
///////////
|
|
||||||
await fd.writeULE32(3); // wires2label type
|
|
||||||
const pMapSize = fd.pos;
|
|
||||||
await fd.writeULE64(0); // Temporally set to 0 length
|
|
||||||
|
|
||||||
|
|
||||||
const arr = new BigArray(NWires);
|
|
||||||
for (let i=0; i<ctx.signals.length; i++) {
|
|
||||||
const outIdx = ctx.signals[i].id;
|
|
||||||
if (ctx.signals[i].e>=0) continue; // If has an alias, continue..
|
|
||||||
assert(typeof outIdx != "undefined", `Signal ${i} does not have index`);
|
|
||||||
if (outIdx>=NWires) continue; // Is a constant or a discarded variable
|
|
||||||
if (typeof arr[ctx.signals[i].id] == "undefined") {
|
|
||||||
arr[outIdx] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i=0; i<arr.length; i++) {
|
|
||||||
await fd.writeULE64(arr[i]);
|
|
||||||
if ((ctx.verbose)&&(i%100000 == 0)) console.log(`writing wire2label map: ${i}/${arr.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapSize = fd.pos - pMapSize - 8;
|
|
||||||
|
|
||||||
// Write sizes
|
|
||||||
await fd.writeULE64(headerSize, pHeaderSize);
|
|
||||||
await fd.writeULE64(constraintsSize, pConstraintsSize);
|
|
||||||
await fd.writeULE64(mapSize, pMapSize);
|
|
||||||
|
|
||||||
await fd.close();
|
|
||||||
|
|
||||||
function writeConstraint(c, ctIdx) {
|
|
||||||
const n8 = ctx.F.n8;
|
|
||||||
const idxA = Object.keys(c.a.coefs);
|
|
||||||
const idxB = Object.keys(c.b.coefs);
|
|
||||||
const idxC = Object.keys(c.c.coefs);
|
|
||||||
const buff = new Uint8Array((idxA.length+idxB.length+idxC.length)*(n8+4) + 12);
|
|
||||||
const buffV = new DataView(buff.buffer);
|
|
||||||
let o=0;
|
|
||||||
|
|
||||||
buffV.setUint32(o, idxA.length, true); o+=4;
|
|
||||||
for (let i=0; i<idxA.length; i++) {
|
|
||||||
const coef = idxA[i];
|
|
||||||
let lSignal = ctx.signals[coef];
|
|
||||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
|
||||||
if (lSignal.id >= NWires) throw new Error("Signal Coef A: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
|
||||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
|
||||||
ctx.F.toRprLE(buff, o, c.a.coefs[coef]); o+=n8;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffV.setUint32(o, idxB.length, true); o+=4;
|
|
||||||
for (let i=0; i<idxB.length; i++) {
|
|
||||||
const coef = idxB[i];
|
|
||||||
let lSignal = ctx.signals[coef];
|
|
||||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
|
||||||
if (lSignal.id >= NWires) throw new Error("Signal Coef B: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
|
||||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
|
||||||
ctx.F.toRprLE(buff, o, c.b.coefs[coef]); o+=n8;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffV.setUint32(o, idxC.length, true); o+=4;
|
|
||||||
for (let i=0; i<idxC.length; i++) {
|
|
||||||
const coef = idxC[i];
|
|
||||||
let lSignal = ctx.signals[coef];
|
|
||||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
|
||||||
if (lSignal.id >= NWires) throw new Error("Signal Coef C: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
|
||||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
|
||||||
ctx.F.toRprLE(buff, o, ctx.F.neg(c.c.coefs[coef])); o+=n8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd.write(buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeBigInt(n, pos) {
|
|
||||||
|
|
||||||
ctx.F.toRprLE(buffBigInt, 0, n);
|
|
||||||
|
|
||||||
await fd.write(buffBigInt, pos);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
const Readable = require("stream").Readable;
|
|
||||||
|
|
||||||
module.exports = function streamFromArrayBin(a) {
|
|
||||||
const rs = Readable();
|
|
||||||
|
|
||||||
let curIndex = 0;
|
|
||||||
|
|
||||||
rs._read = function(size) {
|
|
||||||
if (curIndex >= a.length) {
|
|
||||||
rs.push(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const start = curIndex;
|
|
||||||
const end = Math.min(a.length, curIndex+size);
|
|
||||||
curIndex = end;
|
|
||||||
rs.push(a.slice(start, end));
|
|
||||||
};
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
};
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
const Readable = require("stream").Readable;
|
|
||||||
|
|
||||||
module.exports = function streamFromArrayTxt(ma) {
|
|
||||||
const rs = Readable();
|
|
||||||
|
|
||||||
let curIndex = getFirstIdx(ma);
|
|
||||||
|
|
||||||
rs._read = function() {
|
|
||||||
let res;
|
|
||||||
res = objFromIdx(ma, curIndex);
|
|
||||||
curIndex = nextIdx(curIndex);
|
|
||||||
if (res!==null) {
|
|
||||||
rs.push(res + "\n");
|
|
||||||
} else {
|
|
||||||
rs.push(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
|
|
||||||
|
|
||||||
function getFirstIdx(ma) {
|
|
||||||
if (typeof ma.push !== "function" ) return [];
|
|
||||||
return [0, ...getFirstIdx(ma[0])];
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextIdx(idx) {
|
|
||||||
if (idx == null) return null;
|
|
||||||
if (idx.length == 0) return null;
|
|
||||||
|
|
||||||
const parentIdx = idx.slice(0,-1);
|
|
||||||
|
|
||||||
const itObj = objFromIdx(ma, parentIdx);
|
|
||||||
const newLastIdx = idx[idx.length-1]+1;
|
|
||||||
if (newLastIdx < itObj.length) {
|
|
||||||
const resIdx = idx.slice();
|
|
||||||
resIdx[resIdx.length-1] = newLastIdx;
|
|
||||||
return [...resIdx, ...getFirstIdx(itObj[newLastIdx])];
|
|
||||||
} else {
|
|
||||||
return nextIdx(parentIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function objFromIdx(ma, idx) {
|
|
||||||
if (idx == null) return null;
|
|
||||||
if (idx.length == 0) return ma;
|
|
||||||
if (ma.length == 0) return "";
|
|
||||||
return objFromIdx(ma[idx[0]], idx.slice(1));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
104
src/utils.js
104
src/utils.js
@@ -1,104 +0,0 @@
|
|||||||
const fnv = require("fnv-plus");
|
|
||||||
|
|
||||||
module.exports.ident =ident;
|
|
||||||
|
|
||||||
module.exports.extractSizes =extractSizes;
|
|
||||||
module.exports.flatArray = flatArray;
|
|
||||||
module.exports.csArr = csArr;
|
|
||||||
module.exports.accSizes = accSizes;
|
|
||||||
module.exports.fnvHash = fnvHash;
|
|
||||||
module.exports.sameSizes = sameSizes;
|
|
||||||
module.exports.isDefined = isDefined;
|
|
||||||
module.exports.accSizes2Str = accSizes2Str;
|
|
||||||
module.exports.setUint64 = setUint64;
|
|
||||||
|
|
||||||
function ident(text) {
|
|
||||||
if (typeof text === "string") {
|
|
||||||
let lines = text.split("\n");
|
|
||||||
for (let i=0; i<lines.length; i++) {
|
|
||||||
if (lines[i]) lines[i] = " "+lines[i];
|
|
||||||
}
|
|
||||||
return lines.join("\n");
|
|
||||||
} else if (Array.isArray(text)) {
|
|
||||||
for (let i=0; i<text.length; i++ ) {
|
|
||||||
text[i] = ident(text[i]);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractSizes (o) {
|
|
||||||
if (! Array.isArray(o)) return [];
|
|
||||||
return [o.length, ...extractSizes(o[0])];
|
|
||||||
}
|
|
||||||
|
|
||||||
function flatArray(a) {
|
|
||||||
var res = [];
|
|
||||||
fillArray(res, a);
|
|
||||||
return res;
|
|
||||||
|
|
||||||
function fillArray(res, a) {
|
|
||||||
if (Array.isArray(a)) {
|
|
||||||
for (let i=0; i<a.length; i++) {
|
|
||||||
fillArray(res, a[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.push(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input [1,2,3]
|
|
||||||
// Returns " ,1 ,2, 3"
|
|
||||||
function csArr(_arr) {
|
|
||||||
let S = "";
|
|
||||||
const arr = _arr || [];
|
|
||||||
for (let i=0; i<arr.length; i++) {
|
|
||||||
S = " ,"+arr[i];
|
|
||||||
}
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
function accSizes(_sizes) {
|
|
||||||
const sizes = _sizes || [];
|
|
||||||
const accSizes = [1, 0];
|
|
||||||
for (let i=sizes.length-1; i>=0; i--) {
|
|
||||||
accSizes.unshift(accSizes[0]*sizes[i]);
|
|
||||||
}
|
|
||||||
return accSizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fnvHash(str) {
|
|
||||||
return fnv.hash(str, 64).hex();
|
|
||||||
}
|
|
||||||
|
|
||||||
function sameSizes(s1, s2) {
|
|
||||||
if (!Array.isArray(s1)) return false;
|
|
||||||
if (!Array.isArray(s2)) return false;
|
|
||||||
if (s1.length != s2.length) return false;
|
|
||||||
for (let i=0; i<s1.length; i++) {
|
|
||||||
if (s1[i] != s2[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDefined(v) {
|
|
||||||
return ((typeof(v) != "undefined")&&(v != null));
|
|
||||||
}
|
|
||||||
|
|
||||||
function accSizes2Str(sizes) {
|
|
||||||
if (sizes.length == 2) return "";
|
|
||||||
return `[${sizes[0]/sizes[1]}]`+accSizes2Str(sizes.slice(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUint64(buffV, o, n) {
|
|
||||||
const sLSB = n >>> 0;
|
|
||||||
const sMSB = (n - sLSB) / 0x100000000;
|
|
||||||
buffV.setUint32(o, sLSB , true);
|
|
||||||
buffV.setUint32(o+4, sMSB , true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
|
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
|
||||||
const F1Field = require("ffjavascript").F1Field;
|
|
||||||
const c_tester = require("../index.js").c_tester;
|
|
||||||
const wasm_tester = require("../index.js").wasm_tester;
|
|
||||||
|
|
||||||
const __P__ = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
||||||
|
|
||||||
const Fr = new F1Field(__P__);
|
|
||||||
|
|
||||||
const basicCases = require("./basiccases.json");
|
|
||||||
|
|
||||||
function normalize(o) {
|
|
||||||
if ((typeof(o) == "bigint") || o.isZero !== undefined) {
|
|
||||||
return Fr.e(o);
|
|
||||||
} else if (Array.isArray(o)) {
|
|
||||||
return o.map(normalize);
|
|
||||||
} else if (typeof o == "object") {
|
|
||||||
const res = {};
|
|
||||||
for (let k in o) {
|
|
||||||
res[k] = normalize(o[k]);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return Fr.e(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function doTest(tester, circuit, testVectors) {
|
|
||||||
const cir = await tester(path.join(__dirname, "circuits", circuit));
|
|
||||||
|
|
||||||
for (let i=0; i<testVectors.length; i++) {
|
|
||||||
const w = await cir.calculateWitness(normalize(testVectors[i][0]));
|
|
||||||
// console.log(testVectors[i][0]);
|
|
||||||
// console.log(w);
|
|
||||||
// console.log(testVectors[i][1]);
|
|
||||||
await cir.assertOut(w, normalize(testVectors[i][1]) );
|
|
||||||
}
|
|
||||||
|
|
||||||
await cir.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("basic cases", function () {
|
|
||||||
this.timeout(100000);
|
|
||||||
|
|
||||||
for (let i=0; i<basicCases.length; i++) {
|
|
||||||
it("c/c++ " + basicCases[i].name, async () => {
|
|
||||||
await doTest(c_tester, basicCases[i].circuit, basicCases[i].tv);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i=0; i<basicCases.length; i++) {
|
|
||||||
it("wasm " + basicCases[i].name, async () => {
|
|
||||||
await doTest(wasm_tester, basicCases[i].circuit, basicCases[i].tv);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"name": "inout",
|
|
||||||
"circuit": "inout.circom",
|
|
||||||
"tv": [
|
|
||||||
[{
|
|
||||||
"in1": 1,
|
|
||||||
"in2": [2,3],
|
|
||||||
"in3" : [[4,5], [6,7], [8,9]]
|
|
||||||
}, {
|
|
||||||
"out1": 1,
|
|
||||||
"out2": [2,3],
|
|
||||||
"out3": [[4,5], [6,7],[8,9]]
|
|
||||||
}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "add",
|
|
||||||
"circuit": "add.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [0,0]}, {"out": 0}],
|
|
||||||
[{"in": [0 ,1]}, {"out": 1}],
|
|
||||||
[{"in": [1 ,2]}, {"out": 3}],
|
|
||||||
[{"in": [-1,1]}, {"out": 0}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "add constant",
|
|
||||||
"circuit": "addconst1.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 15}],
|
|
||||||
[{"in": 10}, {"out": 25}],
|
|
||||||
[{"in": -2}, {"out": 13}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "for unrolled",
|
|
||||||
"circuit": "forunrolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [ 0, 1, 2]}],
|
|
||||||
[{"in": 10}, {"out": [10, 11, 12]}],
|
|
||||||
[{"in": -2}, {"out": [-2, -1, 0]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "for rolled",
|
|
||||||
"circuit": "forrolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 0}],
|
|
||||||
[{"in": 10}, {"out": 10}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "while unrolled",
|
|
||||||
"circuit": "whileunrolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [ 0, 1, 2]}],
|
|
||||||
[{"in": 10}, {"out": [10, 11, 12]}],
|
|
||||||
[{"in": -2}, {"out": [-2, -1, 0]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "while rolled",
|
|
||||||
"circuit": "whilerolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 0}],
|
|
||||||
[{"in": 10}, {"out": 10}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "function1",
|
|
||||||
"circuit": "function1.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 3}],
|
|
||||||
[{"in": 10}, {"out": 13}],
|
|
||||||
[{"in": -2}, {"out": 1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "function2",
|
|
||||||
"circuit": "function2.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0 }, {"out": 3}],
|
|
||||||
[{"in": 10}, {"out": 13}],
|
|
||||||
[{"in": -2}, {"out": 1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constants1",
|
|
||||||
"circuit": "constants1.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 42}],
|
|
||||||
[{"in": 10}, {"out": 52}],
|
|
||||||
[{"in": -2}, {"out": 40}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "arrays",
|
|
||||||
"circuit": "arrays.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [ 1, 8, 51]}],
|
|
||||||
[{"in": 10}, {"out": [11, 28, 111]}],
|
|
||||||
[{"in": -2}, {"out": [-1, 4, 39]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "if unrolled",
|
|
||||||
"circuit": "ifunrolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [ 1, 3, 6]}],
|
|
||||||
[{"in": 10}, {"out": [11, 13, 16]}],
|
|
||||||
[{"in": -2}, {"out": [-1, 1, 4]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "if rolled",
|
|
||||||
"circuit": "ifrolled.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [1, 0, 0]}],
|
|
||||||
[{"in": 1}, {"out": [0, 1, 0]}],
|
|
||||||
[{"in": 2}, {"out": [0, 0, 1]}],
|
|
||||||
[{"in": 3}, {"out": [0, 0, 0]}],
|
|
||||||
[{"in": -2}, {"out": [0, 0, 0]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "inc",
|
|
||||||
"circuit": "inc.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [5, 2]}],
|
|
||||||
[{"in": 1}, {"out": [6, 4]}],
|
|
||||||
[{"in": 2}, {"out": [7, 6]}],
|
|
||||||
[{"in": 3}, {"out": [8, 8]}],
|
|
||||||
[{"in": -2}, {"out": [3,-2]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "dec",
|
|
||||||
"circuit": "dec.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": [ 1, -2]}],
|
|
||||||
[{"in": 1}, {"out": [ 2, 0]}],
|
|
||||||
[{"in": 2}, {"out": [ 3, 2]}],
|
|
||||||
[{"in": 3}, {"out": [ 4, 4]}],
|
|
||||||
[{"in": -2}, {"out": [-1, -6]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ops",
|
|
||||||
"circuit": "ops.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [-2, 2]}, {"add": 0, "sub": -4, "mul": -4}],
|
|
||||||
[{"in": [-1, 1]}, {"add": 0, "sub": -2, "mul": -1}],
|
|
||||||
[{"in": [ 0, 0]}, {"add": 0, "sub": 0, "mul": 0}],
|
|
||||||
[{"in": [ 1,-1]}, {"add": 0, "sub": 2, "mul": -1}],
|
|
||||||
[{"in": [ 2,-2]}, {"add": 0, "sub": 4, "mul": -4}],
|
|
||||||
[{"in": [-2,-3]}, {"add": -5, "sub": 1, "mul": 6}],
|
|
||||||
[{"in": [ 2, 3]}, {"add": 5, "sub": -1, "mul": 6}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ops2",
|
|
||||||
"circuit": "ops2.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [-2, 2]}, {"div": -1, "idiv": "10944121435919637611123202872628637544274182200208017171849102093287904247807", "mod": 1}],
|
|
||||||
[{"in": [-1, 1]}, {"div": -1, "idiv": -1, "mod": 0}],
|
|
||||||
[{"in": [ 1,-1]}, {"div": -1, "idiv": 0, "mod": 1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ops3",
|
|
||||||
"circuit": "ops3.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [-2, 2]}, {"neg1": 2, "neg2": -2, "pow": 4}],
|
|
||||||
[{"in": [ 0, 1]}, {"neg1": 0, "neg2": -1, "pow": 0}],
|
|
||||||
[{"in": [ 1,-1]}, {"neg1": -1, "neg2": 1, "pow": 1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comparation ops",
|
|
||||||
"circuit": "opscmp.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [ 8, 9]}, {"lt": 1, "leq": 1, "eq":0, "neq":1, "geq": 0, "gt":0}],
|
|
||||||
[{"in": [-2,-2]}, {"lt": 0, "leq": 1, "eq":1, "neq":0, "geq": 1, "gt":0}],
|
|
||||||
[{"in": [-1,-2]}, {"lt": 0, "leq": 0, "eq":0, "neq":1, "geq": 1, "gt":1}],
|
|
||||||
[{"in": [ 1,-1]}, {"lt": 0, "leq": 0, "eq":0, "neq":1, "geq": 1, "gt":1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Bit ops",
|
|
||||||
"circuit": "opsbit.circom",
|
|
||||||
"tv": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"in": [ 5, 3]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"and": 1,
|
|
||||||
"or": 7,
|
|
||||||
"xor":6,
|
|
||||||
"not1": "7059779437489773633646340506914701874769131765994106666166191815402473914361",
|
|
||||||
"shl": 40,
|
|
||||||
"shr":0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"in": [ 0, 0]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"and": 0,
|
|
||||||
"or": 0,
|
|
||||||
"xor":0,
|
|
||||||
"not1":"7059779437489773633646340506914701874769131765994106666166191815402473914366",
|
|
||||||
"shl": 0,
|
|
||||||
"shr":0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"in": [-1, 1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"and": 0,
|
|
||||||
"or": 0,
|
|
||||||
"xor": 0,
|
|
||||||
"not1": "7059779437489773633646340506914701874769131765994106666166191815402473914367",
|
|
||||||
"shl": "14828463434349501588600065238342573213779232634421927677532012371173334581248",
|
|
||||||
"shr": "10944121435919637611123202872628637544274182200208017171849102093287904247808"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Logical ops",
|
|
||||||
"circuit": "opslog.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [ 5, 0]}, {"and": 0, "or": 1, "not1":0}],
|
|
||||||
[{"in": [ 0, 1]}, {"and": 0, "or": 1, "not1":1}],
|
|
||||||
[{"in": [-1, 9]}, {"and": 1, "or": 1, "not1":0}],
|
|
||||||
[{"in": [ 0, 0]}, {"and": 0, "or": 0, "not1":1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Conditional Ternary operator",
|
|
||||||
"circuit": "condternary.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 0}, {"out": 21}],
|
|
||||||
[{"in": 1}, {"out": 1}],
|
|
||||||
[{"in": 2}, {"out": 23}],
|
|
||||||
[{"in":-1}, {"out": 20}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Compute block",
|
|
||||||
"circuit": "compute.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"x": 1}, {"y": 7}],
|
|
||||||
[{"x": 2}, {"y": 7}],
|
|
||||||
[{"x": 3}, {"y": 11}],
|
|
||||||
[{"x":-1}, {"y": -5}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Component array",
|
|
||||||
"circuit": "componentarray.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 1}, {"out": 1}],
|
|
||||||
[{"in": 2}, {"out": 256}],
|
|
||||||
[{"in": 3}, {"out": 6561}],
|
|
||||||
[{"in":-1}, {"out": 1}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Component array 2d",
|
|
||||||
"circuit": "componentarray2.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": [1,2]}, {"out": [1, 256]}],
|
|
||||||
[{"in": [0,3]}, {"out": [0, 6561]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Constant circuit",
|
|
||||||
"circuit": "constantcircuit.circom",
|
|
||||||
"tv": [
|
|
||||||
[{}, {"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]}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Constant internal circuit",
|
|
||||||
"circuit": "constantinternalcircuit.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 1}, {"out": 5}],
|
|
||||||
[{"in": 0}, {"out": 4}],
|
|
||||||
[{"in": -2}, {"out": 2}],
|
|
||||||
[{"in": 10}, {"out": 14}]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "include",
|
|
||||||
"circuit": "include.circom",
|
|
||||||
"tv": [
|
|
||||||
[{"in": 3}, {"out": 6}],
|
|
||||||
[{"in": 6}, {"out": 15}]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
template Add() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out <== in[0] + in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Add();
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
template AddConst(c) {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
var a = 2;
|
|
||||||
var b = 3;
|
|
||||||
a=a+b;
|
|
||||||
a=a+4;
|
|
||||||
a=a+c;
|
|
||||||
|
|
||||||
out <== 5 + a + in;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It should out <== in + 1+2+3+4+5 = in + 15
|
|
||||||
component main = AddConst(1);
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// arr1
|
|
||||||
|
|
||||||
|
|
||||||
function Add3(arr1, arr2, arr3) {
|
|
||||||
var res[3];
|
|
||||||
|
|
||||||
res[0] = arr1;
|
|
||||||
res[1] = 0;
|
|
||||||
for (var i=0; i<2; i += 1) {
|
|
||||||
res[1] = res[1] + arr2[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
res[2] = 0;
|
|
||||||
for (var i=0; i<2; i++) {
|
|
||||||
for (var j=0; j<3; j += 1) {
|
|
||||||
res[2] = res[2] + arr3[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out[3];
|
|
||||||
|
|
||||||
var c[3] = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
|
|
||||||
var d[3] = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
|
|
||||||
|
|
||||||
out[0] <-- d[0] + c[0];
|
|
||||||
out[0] === in+c[0];
|
|
||||||
|
|
||||||
out[1] <-- d[1]+c[1];
|
|
||||||
// out[1] === (in+in)+3+c[1];
|
|
||||||
out[1] === 2*in+3+c[1];
|
|
||||||
|
|
||||||
out[2] <-- d[2]+c[2];
|
|
||||||
// out[2] === (in+in+in+in+in+in)+12+c[2];
|
|
||||||
out[2] === 6*in+12+c[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
template A() {
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out = 3; // This is an error that compile should detect
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = A();
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
template Square() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out <== in*in;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main(n) {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
component squares[n];
|
|
||||||
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for (i=0; i<n; i++) {
|
|
||||||
squares[i] = Square();
|
|
||||||
if (i==0) {
|
|
||||||
squares[i].in <== in;
|
|
||||||
} else {
|
|
||||||
squares[i].in <== squares[i-1].out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
squares[n-1].out ==> out;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main(3);
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
template Square() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out <== in**2;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main(n, nrounds) {
|
|
||||||
signal input in[n];
|
|
||||||
signal output out[n];
|
|
||||||
|
|
||||||
component squares[n][nrounds];
|
|
||||||
|
|
||||||
for (var i=0; i<n; i++) {
|
|
||||||
for (var r=0; r<nrounds; r++) {
|
|
||||||
squares[i][r] = Square();
|
|
||||||
if (r==0) {
|
|
||||||
squares[i][r].in <== in[i];
|
|
||||||
} else {
|
|
||||||
squares[i][r].in <== squares[i][r-1].out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
squares[i][nrounds-1].out ==> out[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main(2, 3);
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
template X() {
|
|
||||||
signal input x;
|
|
||||||
signal output y;
|
|
||||||
signal x2;
|
|
||||||
signal x3;
|
|
||||||
var a;
|
|
||||||
compute {
|
|
||||||
a = (x*x*x+6)/x;
|
|
||||||
y <-- a;
|
|
||||||
}
|
|
||||||
|
|
||||||
x2 <== x*x;
|
|
||||||
x3 <== x2*x;
|
|
||||||
x*y === x3+6;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = X();
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
template CondTernary() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var a = 3;
|
|
||||||
var b = a==3 ? 1 : 2; // b is 1
|
|
||||||
var c = a!=3 ? 10 : 20; // c is 20
|
|
||||||
var d = b+c; // d is 21
|
|
||||||
|
|
||||||
|
|
||||||
out <-- ((in & 1) != 1) ? in + d : in; // Add 21 if in is pair
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = CondTernary()
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
template H(x) {
|
|
||||||
signal output out[32];
|
|
||||||
var c[8] = [0x6a09e667,
|
|
||||||
0xbb67ae85,
|
|
||||||
0x3c6ef372,
|
|
||||||
0xa54ff53a,
|
|
||||||
0x510e527f,
|
|
||||||
0x9b05688c,
|
|
||||||
0x1f83d9ab,
|
|
||||||
0x5be0cd19];
|
|
||||||
|
|
||||||
for (var i=0; i<32; i++) {
|
|
||||||
out[i] <== (c[x] >> i) & 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = H(1);
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
template Const() {
|
|
||||||
signal output out[2];
|
|
||||||
|
|
||||||
out[0] <== 2;
|
|
||||||
out[1] <== 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
component const = Const();
|
|
||||||
|
|
||||||
out <== const.out[0] + const.out[1] + in;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
template Add(n) {
|
|
||||||
signal input in[n];
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var lc = 0;
|
|
||||||
for (var i=0; i<n; i++) {
|
|
||||||
lc = lc + in[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
out <== lc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function FAdd(a,b) {
|
|
||||||
return a+b;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var o = FAdd(3,4);
|
|
||||||
o = o + FAdd(3,4);
|
|
||||||
o = o + FAdd(3,4); // o = 21
|
|
||||||
|
|
||||||
component A1 = Add(2);
|
|
||||||
A1.in[0] <== in;
|
|
||||||
A1.in[1] <== o;
|
|
||||||
|
|
||||||
component A2 = Add(2);
|
|
||||||
A2.in[0] <== A1.out;
|
|
||||||
A2.in[1] <== o;
|
|
||||||
|
|
||||||
out <== A2.out; // in + 42
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
18
test/circuits/constants_test.circom
Normal file
18
test/circuits/constants_test.circom
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
include "../../circuits/sha256/constants.circom"
|
||||||
|
|
||||||
|
template A() {
|
||||||
|
signal input in;
|
||||||
|
component h0;
|
||||||
|
h0 = K(8);
|
||||||
|
|
||||||
|
var lc = 0;
|
||||||
|
var e = 1;
|
||||||
|
for (var i=0; i<32; i++) {
|
||||||
|
lc = lc + e*h0.out[i];
|
||||||
|
e *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc === in;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = A();
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out[2];
|
|
||||||
|
|
||||||
// First play with variables;
|
|
||||||
|
|
||||||
var c = 3;
|
|
||||||
var d = c--; // d --> 3
|
|
||||||
var e = --c; // e --> 1
|
|
||||||
|
|
||||||
out[0] <== in + e; // in + 1
|
|
||||||
|
|
||||||
// Then play with signals
|
|
||||||
|
|
||||||
c = in;
|
|
||||||
d = c--; //d <-- in;
|
|
||||||
e = --c; // d <-- in-2
|
|
||||||
|
|
||||||
out[1] <== in + e; // 2*in -2
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
template A() {
|
|
||||||
signal a;
|
|
||||||
}
|
|
||||||
|
|
||||||
template B() {
|
|
||||||
component a[2] = A();
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = B();
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
template ForRolled() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var acc = 0;
|
|
||||||
|
|
||||||
for (var i=0; i<in; i = i+1) {
|
|
||||||
acc = acc + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
out <== acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = ForRolled();
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
template ForUnrolled(n) {
|
|
||||||
signal input in;
|
|
||||||
signal output out[n];
|
|
||||||
|
|
||||||
for (var i=0; i<n; i = i+1) {
|
|
||||||
out[i] <== in + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = ForUnrolled(3);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
template A() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var acc = 0;
|
|
||||||
for (var i=0; i<3; i++) {
|
|
||||||
if (i==1) {
|
|
||||||
var accIn = 0;
|
|
||||||
for (var j=0; j<3; j++) {
|
|
||||||
accIn= accIn+1;
|
|
||||||
}
|
|
||||||
acc = acc + accIn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out <== in + acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = A();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
function func1(a,b) {
|
|
||||||
return a+b;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out <== func1(in, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
function fnConst(a,b) {
|
|
||||||
return a+b;
|
|
||||||
}
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var a = fnConst(1,2);
|
|
||||||
out <== in +a;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out[3];
|
|
||||||
|
|
||||||
if (in == 0) {
|
|
||||||
out[0] <-- 1; // TRUE
|
|
||||||
}
|
|
||||||
if (in != 0) {
|
|
||||||
out[0] <-- 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in == 1) {
|
|
||||||
out[1] <-- 1; // TRUE
|
|
||||||
} else {
|
|
||||||
out[1] <-- 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in == 2) {
|
|
||||||
out[2] <-- 1;
|
|
||||||
} else {
|
|
||||||
out[2] <-- 0; // TRUE
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out[3];
|
|
||||||
|
|
||||||
var c = 1;
|
|
||||||
if (c == 1) {
|
|
||||||
out[0] <== in +1; // TRUE
|
|
||||||
}
|
|
||||||
if (c == 0) {
|
|
||||||
out[0] <== in +2;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = c +1;
|
|
||||||
if (c == 2) {
|
|
||||||
out[1] <== in + 3; // TRUE
|
|
||||||
} else {
|
|
||||||
out[1] <== in + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = c +1;
|
|
||||||
if (c == 2) {
|
|
||||||
out[2] <== in + 5;
|
|
||||||
} else {
|
|
||||||
out[2] <== in + 6; // TRUE
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"in1": 1, "in2": [2,3], "in3":[[4,5], [6,7], [8,9]]}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out[2];
|
|
||||||
|
|
||||||
// First play with variables;
|
|
||||||
|
|
||||||
var c = 3;
|
|
||||||
var d = c++; // d --> 3
|
|
||||||
var e = ++c; // e --> 5
|
|
||||||
|
|
||||||
out[0] <== in + e; // in + 5
|
|
||||||
|
|
||||||
// Then play with signals
|
|
||||||
|
|
||||||
c = in;
|
|
||||||
d = c++; //d <-- in;
|
|
||||||
e = ++c; // d <-- in+2
|
|
||||||
|
|
||||||
out[1] <== in + e; // 2*in +2
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
include "included.circom";
|
|
||||||
include "included.circom"; // Include twice is fine. The second one is not included
|
|
||||||
|
|
||||||
template Main() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
component t1 = T1();
|
|
||||||
|
|
||||||
var a = F1(3);
|
|
||||||
|
|
||||||
in ==> t1.in;
|
|
||||||
t1.out + a ==> out; /// out <-- in**2/3+3
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Main();
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
template T1() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
out <== in**2/3;
|
|
||||||
}
|
|
||||||
|
|
||||||
function F1(a) {
|
|
||||||
return a**2/3;
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
template Internal() {
|
|
||||||
signal input in1;
|
|
||||||
signal input in2[2];
|
|
||||||
signal input in3[3][2];
|
|
||||||
|
|
||||||
signal output out1;
|
|
||||||
signal output out2[2];
|
|
||||||
signal output out3[3][2];
|
|
||||||
|
|
||||||
out1 <== in1;
|
|
||||||
out2[0] <== in2[0];
|
|
||||||
out2[1] <== in2[1];
|
|
||||||
|
|
||||||
out3[0][0] <== in3[0][0];
|
|
||||||
out3[0][1] <== in3[0][1];
|
|
||||||
out3[1][0] <== in3[1][0];
|
|
||||||
out3[1][1] <== in3[1][1];
|
|
||||||
out3[2][0] <== in3[2][0];
|
|
||||||
out3[2][1] <== in3[2][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
template InOut() {
|
|
||||||
signal input in1;
|
|
||||||
signal input in2[2];
|
|
||||||
signal input in3[3][2];
|
|
||||||
|
|
||||||
signal output out1;
|
|
||||||
signal output out2[2];
|
|
||||||
signal output out3[3][2];
|
|
||||||
|
|
||||||
component internal = Internal();
|
|
||||||
|
|
||||||
internal.in1 <== in1;
|
|
||||||
internal.in2[0] <== in2[0];
|
|
||||||
internal.in2[1] <== in2[1];
|
|
||||||
internal.in3[0][0] <== in3[0][0];
|
|
||||||
internal.in3[0][1] <== in3[0][1];
|
|
||||||
internal.in3[1][0] <== in3[1][0];
|
|
||||||
internal.in3[1][1] <== in3[1][1];
|
|
||||||
internal.in3[2][0] <== in3[2][0];
|
|
||||||
internal.in3[2][1] <== in3[2][1];
|
|
||||||
|
|
||||||
internal.out1 ==> out1;
|
|
||||||
internal.out2[0] ==> out2[0];
|
|
||||||
internal.out2[1] ==> out2[1];
|
|
||||||
internal.out3[0][0] ==> out3[0][0];
|
|
||||||
internal.out3[0][1] ==> out3[0][1];
|
|
||||||
internal.out3[1][0] ==> out3[1][0];
|
|
||||||
internal.out3[1][1] ==> out3[1][1];
|
|
||||||
internal.out3[2][0] ==> out3[2][0];
|
|
||||||
internal.out3[2][1] ==> out3[2][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = InOut();
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
template X() {
|
|
||||||
signal input i;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var r = 0;
|
|
||||||
for (var n=0; n<i; n++) {
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
|
|
||||||
i === r;
|
|
||||||
out <== i*i;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = X();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
template Ops() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output add;
|
|
||||||
signal output sub;
|
|
||||||
signal output mul;
|
|
||||||
|
|
||||||
add <-- in[0] + in[1];
|
|
||||||
sub <-- in[0] - in[1];
|
|
||||||
mul <-- in[0] * in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Ops();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
template Ops2() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output div;
|
|
||||||
signal output idiv;
|
|
||||||
signal output mod;
|
|
||||||
|
|
||||||
div <-- in[0] / in[1];
|
|
||||||
idiv <-- in[0] \ in[1];
|
|
||||||
mod <-- in[0] % in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Ops2();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
template Ops3() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output neg1;
|
|
||||||
signal output neg2;
|
|
||||||
signal output pow;
|
|
||||||
|
|
||||||
neg1 <-- -in[0];
|
|
||||||
neg2 <-- -in[1];
|
|
||||||
pow <-- in[0] ** in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = Ops3();
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
template OpsBit() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output and;
|
|
||||||
signal output or;
|
|
||||||
signal output xor;
|
|
||||||
signal output not1;
|
|
||||||
signal output shl;
|
|
||||||
signal output shr;
|
|
||||||
|
|
||||||
and <-- in[0] & in[1];
|
|
||||||
or <-- in[0] | in[1];
|
|
||||||
xor <-- in[0] ^ in[1];
|
|
||||||
not1 <-- ~in[0];
|
|
||||||
shl <-- in[0] << in[1];
|
|
||||||
shr <-- in[0] >> in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = OpsBit();
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
template OpsCmp() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output lt;
|
|
||||||
signal output leq;
|
|
||||||
signal output eq;
|
|
||||||
signal output neq;
|
|
||||||
signal output geq;
|
|
||||||
signal output gt;
|
|
||||||
|
|
||||||
lt <-- in[0] < in[1];
|
|
||||||
leq <-- in[0] <= in[1];
|
|
||||||
eq <-- in[0] == in[1];
|
|
||||||
neq <-- in[0] != in[1];
|
|
||||||
geq <-- in[0] >= in[1];
|
|
||||||
gt <-- in[0] > in[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = OpsCmp();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
template OpsLog() {
|
|
||||||
signal input in[2];
|
|
||||||
signal output and;
|
|
||||||
signal output or;
|
|
||||||
signal output not1;
|
|
||||||
|
|
||||||
and <-- in[0] && in[1];
|
|
||||||
or <-- in[0] || in[1];
|
|
||||||
not1 <-- !in[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = OpsLog();
|
|
||||||
2371
test/circuits/out.json
Normal file
2371
test/circuits/out.json
Normal file
File diff suppressed because it is too large
Load Diff
15
test/circuits/sha256_2_test.circom
Normal file
15
test/circuits/sha256_2_test.circom
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
include "../../circuits/sha256/sha256_2.circom";
|
||||||
|
|
||||||
|
template Main() {
|
||||||
|
signal private input a;
|
||||||
|
signal private input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
component sha256_2 = Sha256_2();
|
||||||
|
|
||||||
|
sha256_2.a <== a;
|
||||||
|
sha256_2.b <== b;
|
||||||
|
out <== sha256_2.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = Main();
|
||||||
26
test/circuits/sum_test.circom
Normal file
26
test/circuits/sum_test.circom
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
include "../../circuits/sha256/bitify.circom"
|
||||||
|
include "../../circuits/sha256/binsum.circom"
|
||||||
|
|
||||||
|
template A() {
|
||||||
|
signal private input a;
|
||||||
|
signal input b;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
component n2ba = Num2Bits(32);
|
||||||
|
component n2bb = Num2Bits(32);
|
||||||
|
component sum = BinSum(32,2);
|
||||||
|
component b2n = Bits2Num(32);
|
||||||
|
|
||||||
|
n2ba.in <== a;
|
||||||
|
n2bb.in <== b;
|
||||||
|
|
||||||
|
for (var i=0; i<32; i++) {
|
||||||
|
sum.in[0][i] <== n2ba.out[i];
|
||||||
|
sum.in[1][i] <== n2bb.out[i];
|
||||||
|
b2n.in[i] <== sum.out[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
out <== b2n.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = A();
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
template X() {
|
|
||||||
signal input i;
|
|
||||||
signal input j;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
out <-- i;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
out <-- j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = X();
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
template WhileRolled() {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
var acc = 0;
|
|
||||||
|
|
||||||
var i=0;
|
|
||||||
while (i<in) {
|
|
||||||
acc = acc + 1;
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
out <== acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = WhileRolled();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
template WhileUnrolled(n) {
|
|
||||||
signal input in;
|
|
||||||
signal output out[n];
|
|
||||||
|
|
||||||
var i=0;
|
|
||||||
while (i<n) {
|
|
||||||
out[i] <== in + i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = WhileUnrolled(3);
|
|
||||||
178
test/helpers/sha256.js
Normal file
178
test/helpers/sha256.js
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
/* SHA-256 (FIPS 180-4) implementation in JavaScript (c) Chris Veness 2002-2017 */
|
||||||
|
/* MIT Licence */
|
||||||
|
/* www.movable-type.co.uk/scripts/sha256.html */
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA-256 hash function reference implementation.
|
||||||
|
*
|
||||||
|
* This is an annotated direct implementation of FIPS 180-4, without any optimisations. It is
|
||||||
|
* intended to aid understanding of the algorithm rather than for production use.
|
||||||
|
*
|
||||||
|
* While it could be used where performance is not critical, I would recommend using the ‘Web
|
||||||
|
* Cryptography API’ (developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) for the browser,
|
||||||
|
* or the ‘crypto’ library (nodejs.org/api/crypto.html#crypto_class_hash) in Node.js.
|
||||||
|
*
|
||||||
|
* See csrc.nist.gov/groups/ST/toolkit/secure_hashing.html
|
||||||
|
* csrc.nist.gov/groups/ST/toolkit/examples.html
|
||||||
|
*/
|
||||||
|
class Sha256 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates SHA-256 hash of string.
|
||||||
|
*
|
||||||
|
* @param {string} msg - (Unicode) string to be hashed.
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @param {string} [options.msgFormat=string] - Message format: 'string' for JavaScript string
|
||||||
|
* (gets converted to UTF-8 for hashing); 'hex-bytes' for string of hex bytes ('616263' ≡ 'abc') .
|
||||||
|
* @param {string} [options.outFormat=hex] - Output format: 'hex' for string of contiguous
|
||||||
|
* hex bytes; 'hex-w' for grouping hex bytes into groups of (4 byte / 8 character) words.
|
||||||
|
* @returns {string} Hash of msg as hex character string.
|
||||||
|
*/
|
||||||
|
static hash(msg, options) {
|
||||||
|
const defaults = { msgFormat: 'string', outFormat: 'hex' };
|
||||||
|
const opt = Object.assign(defaults, options);
|
||||||
|
|
||||||
|
// note use throughout this routine of 'n >>> 0' to coerce Number 'n' to unsigned 32-bit integer
|
||||||
|
|
||||||
|
switch (opt.msgFormat) {
|
||||||
|
default: // default is to convert string to UTF-8, as SHA only deals with byte-streams
|
||||||
|
case 'string': msg = utf8Encode(msg); break;
|
||||||
|
case 'hex-bytes':msg = hexBytesToString(msg); break; // mostly for running tests
|
||||||
|
}
|
||||||
|
|
||||||
|
// constants [§4.2.2]
|
||||||
|
const K = [
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ];
|
||||||
|
|
||||||
|
// initial hash value [§5.3.3]
|
||||||
|
const H = [
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];
|
||||||
|
|
||||||
|
// PREPROCESSING [§6.2.1]
|
||||||
|
|
||||||
|
msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
|
||||||
|
|
||||||
|
// convert string msg into 512-bit blocks (array of 16 32-bit integers) [§5.2.1]
|
||||||
|
const l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
|
||||||
|
const N = Math.ceil(l/16); // number of 16-integer (512-bit) blocks required to hold 'l' ints
|
||||||
|
const M = new Array(N); // message M is N×16 array of 32-bit integers
|
||||||
|
|
||||||
|
for (let i=0; i<N; i++) {
|
||||||
|
M[i] = new Array(16);
|
||||||
|
for (let j=0; j<16; j++) { // encode 4 chars per integer (64 per block), big-endian encoding
|
||||||
|
M[i][j] = (msg.charCodeAt(i*64+j*4+0)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16)
|
||||||
|
| (msg.charCodeAt(i*64+j*4+2)<< 8) | (msg.charCodeAt(i*64+j*4+3)<< 0);
|
||||||
|
} // note running off the end of msg is ok 'cos bitwise ops on NaN return 0
|
||||||
|
}
|
||||||
|
// add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
|
||||||
|
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts
|
||||||
|
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
|
||||||
|
const lenHi = ((msg.length-1)*8) / Math.pow(2, 32);
|
||||||
|
const lenLo = ((msg.length-1)*8) >>> 0;
|
||||||
|
M[N-1][14] = Math.floor(lenHi);
|
||||||
|
M[N-1][15] = lenLo;
|
||||||
|
|
||||||
|
// HASH COMPUTATION [§6.2.2]
|
||||||
|
|
||||||
|
for (let i=0; i<N; i++) {
|
||||||
|
const W = new Array(64);
|
||||||
|
|
||||||
|
// 1 - prepare message schedule 'W'
|
||||||
|
for (let t=0; t<16; t++) W[t] = M[i][t];
|
||||||
|
for (let t=16; t<64; t++) {
|
||||||
|
W[t] = (Sha256.σ1(W[t-2]) + W[t-7] + Sha256.σ0(W[t-15]) + W[t-16]) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value
|
||||||
|
let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
|
||||||
|
|
||||||
|
// 3 - main loop (note '>>> 0' for 'addition modulo 2^32')
|
||||||
|
for (let t=0; t<64; t++) {
|
||||||
|
const T1 = h + Sha256.Σ1(e) + Sha256.Ch(e, f, g) + K[t] + W[t];
|
||||||
|
const T2 = Sha256.Σ0(a) + Sha256.Maj(a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = (d + T1) >>> 0;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = (T1 + T2) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 - compute the new intermediate hash value (note '>>> 0' for 'addition modulo 2^32')
|
||||||
|
H[0] = (H[0]+a) >>> 0;
|
||||||
|
H[1] = (H[1]+b) >>> 0;
|
||||||
|
H[2] = (H[2]+c) >>> 0;
|
||||||
|
H[3] = (H[3]+d) >>> 0;
|
||||||
|
H[4] = (H[4]+e) >>> 0;
|
||||||
|
H[5] = (H[5]+f) >>> 0;
|
||||||
|
H[6] = (H[6]+g) >>> 0;
|
||||||
|
H[7] = (H[7]+h) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert H0..H7 to hex strings (with leading zeros)
|
||||||
|
for (let h=0; h<H.length; h++) H[h] = ('00000000'+H[h].toString(16)).slice(-8);
|
||||||
|
|
||||||
|
// concatenate H0..H7, with separator if required
|
||||||
|
const separator = opt.outFormat=='hex-w' ? ' ' : '';
|
||||||
|
|
||||||
|
return H.join(separator);
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
function utf8Encode(str) {
|
||||||
|
try {
|
||||||
|
return new TextEncoder().encode(str, 'utf-8').reduce((prev, curr) => prev + String.fromCharCode(curr), '');
|
||||||
|
} catch (e) { // no TextEncoder available?
|
||||||
|
return unescape(encodeURIComponent(str)); // monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexBytesToString(hexStr) { // convert string of hex numbers to a string of chars (eg '616263' -> 'abc').
|
||||||
|
const str = hexStr.replace(' ', ''); // allow space-separated groups
|
||||||
|
return str=='' ? '' : str.match(/.{2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates right (circular right shift) value x by n positions [§3.2.4].
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static ROTR(n, x) {
|
||||||
|
return (x >>> n) | (x << (32-n));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical functions [§4.1.2].
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static Σ0(x) { return Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x); }
|
||||||
|
static Σ1(x) { return Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x); }
|
||||||
|
static σ0(x) { return Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x>>>3); }
|
||||||
|
static σ1(x) { return Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x>>>10); }
|
||||||
|
static Ch(x, y, z) { return (x & y) ^ (~x & z); } // 'choice'
|
||||||
|
static Maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } // 'majority'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
if (typeof module != 'undefined' && module.exports) module.exports = Sha256; // ≡ export default Sha256
|
||||||
|
|
||||||
4
test/input_sum_test.json
Normal file
4
test/input_sum_test.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"a": "111",
|
||||||
|
"b": "222"
|
||||||
|
}
|
||||||
82
test/sha256.js
Normal file
82
test/sha256.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
const chai = require("chai");
|
||||||
|
const path = require("path");
|
||||||
|
const zkSnark = require("zksnark");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
const compiler = require("../index.js");
|
||||||
|
|
||||||
|
const assert = chai.assert;
|
||||||
|
|
||||||
|
const sha256 = require("./helpers/sha256");
|
||||||
|
const bigInt = require("big-integer");
|
||||||
|
|
||||||
|
function hexBits(cir, witness, sig, nBits) {
|
||||||
|
let v = bigInt(0);
|
||||||
|
for (let i=nBits-1; i>=0; i--) {
|
||||||
|
v = v.shiftLeft(1);
|
||||||
|
const name = sig+"["+i+"]";
|
||||||
|
const idx = cir.getSignalIdx(name);
|
||||||
|
const vbit = bigInt(witness[idx].toString());
|
||||||
|
if (vbit.equals(bigInt(1))) {
|
||||||
|
v = v.add(bigInt(1));
|
||||||
|
} else if (vbit.equals(bigInt(0))) {
|
||||||
|
v;
|
||||||
|
} else {
|
||||||
|
console.log("Not Binary: "+name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v.toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("SHA256 test", () => {
|
||||||
|
it("Should create a constant circuit", async () => {
|
||||||
|
|
||||||
|
const cirDef = await compiler(path.join(__dirname, "circuits", "constants_test.circom"));
|
||||||
|
assert.equal(cirDef.nVars, 2);
|
||||||
|
|
||||||
|
const circuit = new zkSnark.Circuit(cirDef);
|
||||||
|
|
||||||
|
const witness = circuit.calculateWitness({ "in": "0xd807aa98" });
|
||||||
|
|
||||||
|
assert(witness[0].equals(zkSnark.bigInt(1)));
|
||||||
|
assert(witness[1].equals(zkSnark.bigInt("0xd807aa98")));
|
||||||
|
});
|
||||||
|
it("Should create a sum circuit", async () => {
|
||||||
|
|
||||||
|
const cirDef = await compiler(path.join(__dirname, "circuits", "sum_test.circom"));
|
||||||
|
assert.equal(cirDef.nVars, 101);
|
||||||
|
|
||||||
|
const circuit = new zkSnark.Circuit(cirDef);
|
||||||
|
|
||||||
|
const witness = circuit.calculateWitness({ "a": "111", "b": "222" });
|
||||||
|
|
||||||
|
assert(witness[0].equals(zkSnark.bigInt(1)));
|
||||||
|
assert(witness[1].equals(zkSnark.bigInt("333")));
|
||||||
|
});
|
||||||
|
it("Should calculate a hash", async () => {
|
||||||
|
const cirDef = await compiler(path.join(__dirname, "circuits", "sha256_2_test.circom"));
|
||||||
|
const circuit = new zkSnark.Circuit(cirDef);
|
||||||
|
|
||||||
|
console.log("Vars: "+circuit.nVars);
|
||||||
|
console.log("Constraints: "+circuit.nConstraints);
|
||||||
|
|
||||||
|
const witness = circuit.calculateWitness({ "a": "1", "b": "2" });
|
||||||
|
|
||||||
|
const b = new Buffer.alloc(54);
|
||||||
|
b[26] = 1;
|
||||||
|
b[53] = 2;
|
||||||
|
|
||||||
|
const hash = crypto.createHash("sha256")
|
||||||
|
.update(b)
|
||||||
|
.digest("hex");
|
||||||
|
const r = "0x" + hash.slice(10);
|
||||||
|
|
||||||
|
const hash2 = sha256.hash(b.toString("hex"), {msgFormat: "hex-bytes"});
|
||||||
|
|
||||||
|
assert.equal(hash, hash2);
|
||||||
|
|
||||||
|
assert(witness[1].equals(zkSnark.bigInt(r)));
|
||||||
|
}).timeout(1000000);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user