mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 11:16:42 +01:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9b384681b | ||
|
|
eb8cb0af74 | ||
|
|
ef899e618b | ||
|
|
8f63d18ff4 | ||
|
|
6c1a3e7687 | ||
|
|
111c91c70d | ||
|
|
a8d597d8c5 | ||
|
|
3a9766a008 | ||
|
|
20058a38d6 | ||
|
|
f6092e3944 | ||
|
|
e11e6768e4 | ||
|
|
63fd72cdc7 | ||
|
|
da969a5e16 | ||
|
|
b564201170 | ||
|
|
e62c1cdbc3 | ||
|
|
ec0e7f421b | ||
|
|
afa8201c2c | ||
|
|
1f94f7f3ec | ||
|
|
eaf4396cb3 | ||
|
|
2a45647274 | ||
|
|
305bc7456f | ||
|
|
ff1c12bcc3 | ||
|
|
fbcc753bc1 | ||
|
|
1e3d1235cb | ||
|
|
7b0b203c60 | ||
|
|
80846667ea | ||
|
|
7181c372d9 | ||
|
|
aecc28a79b | ||
|
|
0be08d67b0 | ||
|
|
6cdb006909 | ||
|
|
f4bbcfd90c | ||
|
|
93330f065b | ||
|
|
66291a0efe | ||
|
|
83c95b5188 | ||
|
|
13c4c81a0f | ||
|
|
51ff27b9c6 | ||
|
|
6985892f86 | ||
|
|
bacb7afde7 | ||
|
|
d04eff6c0d | ||
|
|
230894921e | ||
|
|
64029e1842 | ||
|
|
700412f23d | ||
|
|
832077fbe9 | ||
|
|
0df0ac712d | ||
|
|
67a35ee400 | ||
|
|
680e3fe139 | ||
|
|
f05c4e1338 | ||
|
|
597deb1eaa | ||
|
|
7a1c606ca6 | ||
|
|
6642d4cf93 | ||
|
|
da0c60a919 | ||
|
|
534efcf355 | ||
|
|
a43154241e | ||
|
|
859c98d2a4 | ||
|
|
8048a5ef7d | ||
|
|
b7a41cda14 | ||
|
|
34049f2fbd | ||
|
|
a602551ee5 | ||
|
|
4d5760ff67 | ||
|
|
4a8bcff3da | ||
|
|
b8068e8d05 | ||
|
|
54092044ae | ||
|
|
5fccdd6ef1 |
@@ -1,7 +1,4 @@
|
||||
module.exports = {
|
||||
"plugins": [
|
||||
"mocha"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
@@ -27,7 +24,6 @@ module.exports = {
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"mocha/no-exclusive-tests": "error"
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -61,3 +61,9 @@ typings/
|
||||
.next
|
||||
|
||||
tmp
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Workspace files are user-specific
|
||||
*.sublime-workspace
|
||||
|
||||
|
||||
2
COPYING
2
COPYING
@@ -1,7 +1,7 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2020 0Kims Association <https://0kims.org>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
24
Project.sublime-project
Normal file
24
Project.sublime-project
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".",
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"SublimeAnarchyDebug": {
|
||||
"debug": {
|
||||
"executable": "${project_path}/test/circuits/add",
|
||||
"params": [
|
||||
"addin.json",
|
||||
"out.bin",
|
||||
],
|
||||
"path": [
|
||||
],
|
||||
"environment": [
|
||||
],
|
||||
"working_dir": "${project_path}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ In particular, it is designed to work in [zksnarks JavaScript library](https://g
|
||||
|
||||
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
|
||||
|
||||
Creation of a circuit. This is an example of a NAND door:
|
||||
@@ -175,7 +177,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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
86
TUTORIAL.md
86
TUTORIAL.md
@@ -1,23 +1,22 @@
|
||||
# circom and snarkjs tutorial
|
||||
|
||||
This tutorial will guide you in creating your first Zero Knowledge zkSnark circuit. It will navegate across the various techniques to write circuits and it will show you how to create proofs and verify them off-chain and on-chain on Ethereum.
|
||||
This tutorial will guide you in creating your first Zero Knowledge zkSnark circuit. It will take you through the various techniques to write circuits, and will show you how to create proofs and verify them 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` in your laptop.
|
||||
|
||||
Last stable version of `Node.js` (Or 8.12.0) works just fine. But if you install the latest current version `Node.js` (10.12.0) you will see significant performance increase. This is because last versions of node includes Big Integer Libraries nativelly. The `snarkjs` library makes use of this feature if available, and this improves the performance x10 (!).
|
||||
If you don't have it installed yet, you need to install `Node.js`.
|
||||
|
||||
The last stable version of `Node.js` (or 8.12.0) works just fine, but if you install the latest current version `Node.js` (10.12.0) you will see a significant increase in performance. This is because last versions of node includes Big Integer Libraries nativelly. The `snarkjs` library makes use of this feature if available, and this improves the performance x10 (!).
|
||||
|
||||
### 1.2 Install **circom** and **snarkjs**
|
||||
|
||||
Just run:
|
||||
Run:
|
||||
|
||||
```sh
|
||||
npm install -g circom
|
||||
npm install -g circom_runtime
|
||||
npm install -g snarkjs
|
||||
```
|
||||
|
||||
@@ -57,17 +56,19 @@ The only thing that the circuit does is forcing the signal `c` to be the value o
|
||||
|
||||
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.
|
||||
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 -o circuit.json
|
||||
circom circuit.circom --r1cs --wasm --sym
|
||||
```
|
||||
|
||||
to compile the circuit to a file named `circuit.json`
|
||||
The -r optin will generate `circuit.r1cs` ( The r1cs constraint system of the circuit in binary format)
|
||||
The -w will generate `circuit.wasm` (The wasm code to generate the witness)
|
||||
The -s will generate `circuit.sym` (This is the 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*
|
||||
@@ -84,13 +85,13 @@ snarkjs --help
|
||||
To show general statistics of this circuit, you can run:
|
||||
|
||||
```sh
|
||||
snarkjs info -c circuit.json
|
||||
snarkjs info -r circuit.r1cs
|
||||
```
|
||||
|
||||
You can also print the constraints of the circuit by running:
|
||||
|
||||
```sh
|
||||
snarkjs printconstraints -c circuit.json
|
||||
snarkjs printconstraints -r circuit.r1cs -s circuit.sym
|
||||
```
|
||||
|
||||
|
||||
@@ -103,7 +104,7 @@ Ok, let's run a setup for our circuit:
|
||||
snarkjs setup
|
||||
```
|
||||
|
||||
> By default `snarkjs` will look for and use `circuit.json`. You can always specify a different circuit file by adding `-c <circuit JSON file name>`
|
||||
> 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 in the form of 2 files: `proving_key.json` and `verification_key.json`
|
||||
|
||||
@@ -111,14 +112,13 @@ The output of the setup will in the form of 2 files: `proving_key.json` and `ver
|
||||
|
||||
Before creating any proof, we need to calculate all the signals of the circuit that match (all) the constrains of the circuit.
|
||||
|
||||
`snarkjs` calculates these 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*.
|
||||
`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 but without revealing any of the signals except the public inputs plus the outputs.
|
||||
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 plus the outputs.
|
||||
|
||||
For example, Imagine that you want to prove that you are able to factor 33 that means that you know two numbers `a` and `b` that when you multiply them, it results in 33.
|
||||
|
||||
> Of course you can always use one and the same number as `a` and `b`. We will deal with this problem later.
|
||||
For example, imagine you want to prove you are able to factor 33. It means that you know two numbers `a` and `b` and when you multiply them, it results in 33.
|
||||
|
||||
> Of course you can always use 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.
|
||||
|
||||
@@ -128,12 +128,16 @@ Let's create a file named `input.json`
|
||||
{"a": 3, "b": 11}
|
||||
```
|
||||
|
||||
And now let's calculate the witness:
|
||||
Now let's calculate the witness:
|
||||
|
||||
```sh
|
||||
snarkjs calculatewitness
|
||||
snarkjs --wasm circuit.wasm --input input.json --witness witness.json
|
||||
```
|
||||
|
||||
`calcwit` is part of the circom_runtime package and it's just a wrapper in JS to call the wasm module.
|
||||
|
||||
You can use `circom_runtime` from your own project to calulate the witness.
|
||||
|
||||
You may want to take a look at `witness.json` file with all the signals.
|
||||
|
||||
### Create the proof
|
||||
@@ -144,7 +148,7 @@ Now that we have the witness generated, we can create the proof.
|
||||
snarkjs proof
|
||||
```
|
||||
|
||||
This command will use the `prooving_key.json` and the `witness.json` files by default to generate `proof.json` and `public.json`
|
||||
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.
|
||||
|
||||
@@ -159,11 +163,10 @@ snarkjs verify
|
||||
|
||||
This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid.
|
||||
|
||||
Here we are veifying that we know a witness that the public inputs and the outputs matches the ones in the `public.json` file.
|
||||
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 an `OK` in the screen or `INVALID` otherwise.
|
||||
|
||||
If the proof is ok, you will see `OK` or `INVALID` if not ok.
|
||||
|
||||
### Generate the solidity verifier
|
||||
|
||||
@@ -171,11 +174,11 @@ If the proof is ok, you will see an `OK` in the screen or `INVALID` otherwise.
|
||||
snarkjs generateverifier
|
||||
```
|
||||
|
||||
This command will take the `verification_key.json` and generate a solidity code in `verifier.sol` file.
|
||||
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 in remix.
|
||||
You can take the code in `verifier.sol` and cut and paste it in remix.
|
||||
|
||||
This code contains two contracts: Pairings and Verifier. You just need to deploy the `Verifier` contract.
|
||||
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.
|
||||
|
||||
@@ -186,7 +189,7 @@ The verifier contract deployed in the last step has a `view` function called `ve
|
||||
|
||||
This function will return true if the proof and the inputs are valid.
|
||||
|
||||
To facilitiate the call, you can use snarkjs to generate the parameters of the call by typing:
|
||||
To facilitate the call, you can use snarkjs to generate the parameters of the call by typing:
|
||||
|
||||
```sh
|
||||
snarkjs generatecall
|
||||
@@ -196,16 +199,16 @@ Just cut and paste the output to the parameters field of the `verifyProof` metho
|
||||
|
||||
If every thing works ok, this method should return true.
|
||||
|
||||
If you just change any bit in the parameters, you can check that the result will be false.
|
||||
If you change any bit in the parameters, the result will be verifiably false.
|
||||
|
||||
|
||||
## Bonus track
|
||||
|
||||
We can fix the circuit to not accept one as any of the 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.
|
||||
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 one.
|
||||
That means that `(a-1)*inv = 1` will be inpossible to match if `a` is 1.
|
||||
|
||||
We just calculate inv by `1/(a-1)`
|
||||
|
||||
@@ -231,20 +234,19 @@ template Multiplier() {
|
||||
component main = Multiplier();
|
||||
```
|
||||
|
||||
A nice thing of circom language is that you can split a <== into two independent acions: <-- and ===
|
||||
A nice thing of the circom language is that you can split a <== into two independent actions: <-- and ===
|
||||
|
||||
The <-- and --> operators Just assign a value to a signal without creating any constraints.
|
||||
The <-- and --> operators assign a value to a signal without creating any constraints.
|
||||
|
||||
The === operator just adds a constraint without assigning any value to any signal.
|
||||
The === operator adds a constraint without assigning any value to any signal.
|
||||
|
||||
The circuit has also another problem and it's that the operation works in Zr, so we need to guarantee too that the multiplication does not overflow. This can be done by binarizing the inputs and checking the ranges, but we will reserve it for future tutorials.
|
||||
The circuit also has another problem: the operation works in Zr, 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.
|
||||
## 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 a library with many basic circuits lib binaritzations, comparators, eddsa, hashes, merkle trees etc [here](https://github.com/iden3/circomlib) (Work in progress).
|
||||
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 Jub curve [here](https://github.com/iden3/circomlib) (Work in progress).
|
||||
@@ -252,14 +254,8 @@ Or a exponentiation in the Baby Jub curve [here](https://github.com/iden3/circom
|
||||
|
||||
# Final note
|
||||
|
||||
There is nothing worst 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 works needs to be done. Please have it present if you are doing anything serious with it.
|
||||
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 worthy!.
|
||||
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!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
44
circuit.json
44
circuit.json
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"mainCode": "{\n}\n",
|
||||
"signalName2Idx": {
|
||||
"one": 0,
|
||||
"main.out": 1
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"name": "main",
|
||||
"params": {},
|
||||
"template": "A",
|
||||
"inputSignals": 0
|
||||
}
|
||||
],
|
||||
"componentName2Idx": {
|
||||
"main": 0
|
||||
},
|
||||
"signals": [
|
||||
{
|
||||
"names": [
|
||||
"one"
|
||||
],
|
||||
"triggerComponents": []
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"main.out"
|
||||
],
|
||||
"triggerComponents": []
|
||||
}
|
||||
],
|
||||
"constraints": [],
|
||||
"templates": {
|
||||
"A": "function(ctx) {\n ctx.setSignal(\"out\", [], \"3\");\n ctx.assert(ctx.getSignal(\"out\", []), \"3\");\n}\n"
|
||||
},
|
||||
"functions": {},
|
||||
"nPrvInputs": 0,
|
||||
"nPubInputs": 0,
|
||||
"nInputs": 0,
|
||||
"nOutputs": 0,
|
||||
"nVars": 1,
|
||||
"nConstants": 1,
|
||||
"nSignals": 2
|
||||
}
|
||||
100
cli.js
100
cli.js
@@ -23,6 +23,7 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
const compiler = require("./src/compiler");
|
||||
|
||||
@@ -30,11 +31,27 @@ const version = require("./package").version;
|
||||
|
||||
const argv = require("yargs")
|
||||
.version(version)
|
||||
.usage("circom [input source circuit file] -o [output definition circuit file]")
|
||||
.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]")
|
||||
.alias("o", "output")
|
||||
.alias("c", "csource")
|
||||
.alias("w", "wasm")
|
||||
.alias("t", "wat")
|
||||
.alias("s", "sym")
|
||||
.alias("r", "r1cs")
|
||||
.alias("p", "prime")
|
||||
.alias("n", "newThreadTemplates")
|
||||
.help("h")
|
||||
.alias("h", "help")
|
||||
.alias("v", "verbose")
|
||||
.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
|
||||
This program comes with ABSOLUTELY NO WARRANTY;
|
||||
This is free software, and you are welcome to redistribute it
|
||||
@@ -54,11 +71,82 @@ if (argv._.length == 0) {
|
||||
}
|
||||
|
||||
const fullFileName = path.resolve(process.cwd(), inputFile);
|
||||
const outName = argv.output ? argv.output : "circuit.json";
|
||||
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";
|
||||
|
||||
compiler(fullFileName).then( (cir) => {
|
||||
fs.writeFileSync(outName, JSON.stringify(cir, null, 1), "utf8");
|
||||
process.exit(0);
|
||||
const options = {};
|
||||
options.reduceConstraints = !argv.fast;
|
||||
options.verbose = argv.verbose || false;
|
||||
options.sanityCheck = argv.sanitycheck;
|
||||
options.prime = argv.prime || bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
if (argv.csource) {
|
||||
options.cSourceWriteStream = fs.createWriteStream(cSourceName);
|
||||
}
|
||||
if (argv.wasm) {
|
||||
options.wasmWriteStream = fs.createWriteStream(wasmName);
|
||||
}
|
||||
if (argv.wat) {
|
||||
options.watWriteStream = fs.createWriteStream(watName);
|
||||
}
|
||||
if (argv.r1cs) {
|
||||
options.r1csFileName = r1csName;
|
||||
}
|
||||
if (argv.sym) {
|
||||
options.symWriteStream = fs.createWriteStream(symName);
|
||||
}
|
||||
if (argv.newThreadTemplates) {
|
||||
options.newThreadTemplates = new RegExp(argv.newThreadTemplates);
|
||||
}
|
||||
|
||||
compiler(fullFileName, options).then( () => {
|
||||
let cSourceDone = false;
|
||||
let wasmDone = false;
|
||||
let symDone = false;
|
||||
let watDone = false;
|
||||
if (options.cSourceWriteStream) {
|
||||
options.cSourceWriteStream.on("finish", () => {
|
||||
cSourceDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
cSourceDone = true;
|
||||
}
|
||||
if (options.wasmWriteStream) {
|
||||
options.wasmWriteStream.on("finish", () => {
|
||||
wasmDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
wasmDone = true;
|
||||
}
|
||||
if (options.watWriteStream) {
|
||||
options.watWriteStream.on("finish", () => {
|
||||
watDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
watDone = true;
|
||||
}
|
||||
if (options.symWriteStream) {
|
||||
options.symWriteStream.on("finish", () => {
|
||||
symDone = true;
|
||||
finishIfDone();
|
||||
});
|
||||
} else {
|
||||
symDone = true;
|
||||
}
|
||||
function finishIfDone() {
|
||||
if ((cSourceDone)&&(symDone)&&(wasmDone)&&(watDone)) {
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}, (err) => {
|
||||
// console.log(err);
|
||||
console.log(err.stack);
|
||||
|
||||
BIN
doc/lc_example.monopic
Normal file
BIN
doc/lc_example.monopic
Normal file
Binary file not shown.
654
doc/r1cs_bin_format.md
Normal file
654
doc/r1cs_bin_format.md
Normal file
@@ -0,0 +1,654 @@
|
||||
# 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 +1,4 @@
|
||||
module.exports = require("./src/compiler.js");
|
||||
module.exports.compiler = 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;
|
||||
|
||||
1108
package-lock.json
generated
1108
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.0.25",
|
||||
"version": "0.5.0",
|
||||
"description": "Language to generate logic circuits",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
@@ -30,14 +30,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"big-integer": "^1.6.32",
|
||||
"optimist": "^0.6.1",
|
||||
"yargs": "^12.0.2"
|
||||
"chai": "^4.2.0",
|
||||
"circom_runtime": "0.0.2",
|
||||
"ffiasm": "0.0.2",
|
||||
"ffjavascript": "0.0.3",
|
||||
"ffwasm": "0.0.5",
|
||||
"fnv-plus": "^1.3.1",
|
||||
"r1csfile": "0.0.2",
|
||||
"tmp-promise": "^2.0.2",
|
||||
"wasmbuilder": "0.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint": "^6.8.0",
|
||||
"jison": "^0.4.18",
|
||||
"snarkjs": "0.1.9"
|
||||
"yargs": "^15.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ if { return 'if'; }
|
||||
else { return 'else'; }
|
||||
for { return 'for'; }
|
||||
while { return 'while'; }
|
||||
compute { return 'compute'; }
|
||||
do { return 'do'; }
|
||||
return { return 'return'; }
|
||||
include { return 'include'; }
|
||||
@@ -84,6 +85,7 @@ include { return 'include'; }
|
||||
\& { return '&'; }
|
||||
\| { return '|'; }
|
||||
\! { return '!'; }
|
||||
\~ { return '~'; }
|
||||
\< { return '<'; }
|
||||
\> { return '>'; }
|
||||
\! { return '!'; }
|
||||
@@ -198,6 +200,10 @@ statment
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| computeStatment
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| returnStatment
|
||||
{
|
||||
$$ = $1;
|
||||
@@ -302,6 +308,14 @@ doWhileStatment
|
||||
}
|
||||
;
|
||||
|
||||
computeStatment
|
||||
: 'compute' statment
|
||||
{
|
||||
$$ = { type: "COMPUTE", body: $2 };
|
||||
setLines($$, @1, @2);
|
||||
}
|
||||
;
|
||||
|
||||
returnStatment
|
||||
: 'return' expression ';'
|
||||
{
|
||||
@@ -514,7 +528,7 @@ e12
|
||||
: e12 '^' e11
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
|
||||
$$ = { type: "NUMBER", value: $1.value.xor($3.value).and(__MASK__) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: "^", values: [$1, $3] };
|
||||
}
|
||||
|
||||
334
parser/jaz.js
334
parser/jaz.js
File diff suppressed because one or more lines are too long
627
ports/c/builder.js
Normal file
627
ports/c/builder.js
Normal file
@@ -0,0 +1,627 @@
|
||||
const streamFromMultiArray = require("../../src/streamfromarray_txt.js");
|
||||
const bigInt = require("big-integer");
|
||||
const utils = require("../../src/utils");
|
||||
const assert = require("assert");
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
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 == "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() {
|
||||
this.hashMaps={};
|
||||
this.componentEntriesTables={};
|
||||
this.sizes ={};
|
||||
this.constants = [];
|
||||
this.functions = [];
|
||||
this.components = [];
|
||||
this.usedConstants = {};
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
this.componentEntriesTables[name] = cet;
|
||||
}
|
||||
|
||||
addSizes(name, accSizes) {
|
||||
this.sizes[name] = accSizes;
|
||||
}
|
||||
|
||||
addConstant(c) {
|
||||
c = bigInt(c);
|
||||
const cS = c.toString();
|
||||
if (this.usedConstants[cS]) 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.h\"",
|
||||
"#include \"calcwit.h\"",
|
||||
`#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()}"`,
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
_buildHashMaps(code) {
|
||||
|
||||
code.push("// Hash Maps ");
|
||||
for (let hmName in this.hashMaps ) {
|
||||
const hm = this.hashMaps[hmName];
|
||||
|
||||
let c = `Circom_HashEntry ${hmName}[256] = {`;
|
||||
for (let i=0; i<256; i++) {
|
||||
c += i>0 ? "," : "";
|
||||
if (hm[i]) {
|
||||
c += `{0x${hm[i][0]}LL, ${hm[i][1]}} /* ${hm[i][2]} */`;
|
||||
} else {
|
||||
c += "{0,0}";
|
||||
}
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_buildComponentEntriesTables(code) {
|
||||
code.push("// Component Entry tables");
|
||||
for (let cetName in this.componentEntriesTables) {
|
||||
const cet = this.componentEntriesTables[cetName];
|
||||
|
||||
code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`);
|
||||
for (let j=0; j<cet.length; j++) {
|
||||
const ty = cet[j].type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code.push(` ${j>0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
}
|
||||
|
||||
_buildSizes(code) {
|
||||
code.push("// Sizes");
|
||||
for (let sName in this.sizes) {
|
||||
const accSizes = this.sizes[sName];
|
||||
|
||||
let c = `Circom_Size ${sName}[${accSizes.length}] = {`;
|
||||
for (let i=0; i<accSizes.length; i++) {
|
||||
if (i>0) c += ",";
|
||||
c += accSizes[i];
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
_buildConstants(code) {
|
||||
const self = this;
|
||||
const n64 = Math.floor((self.header.P.bitLength() - 1) / 64)+1;
|
||||
const R = bigInt.one.shiftLeft(n64*64);
|
||||
|
||||
code.push("// Constants");
|
||||
code.push(`FrElement _constants[${self.constants.length}] = {`);
|
||||
for (let i=0; i<self.constants.length; i++) {
|
||||
code.push((i>0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}");
|
||||
}
|
||||
code.push("};");
|
||||
|
||||
function number2Code(n) {
|
||||
if (n.lt(bigInt("80000000", 16)) ) {
|
||||
return addShortMontgomeryPositive(n);
|
||||
}
|
||||
if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
|
||||
return addShortMontgomeryNegative(n);
|
||||
}
|
||||
return addLongMontgomery(n);
|
||||
|
||||
|
||||
function addShortMontgomeryPositive(a) {
|
||||
return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
|
||||
function addShortMontgomeryNegative(a) {
|
||||
const b = a.minus(self.header.P);
|
||||
return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
function addLongMontgomery(a) {
|
||||
return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
}
|
||||
|
||||
function getLongString(a) {
|
||||
let r = bigInt(a);
|
||||
let S = "";
|
||||
let i = 0;
|
||||
while (!r.isZero()) {
|
||||
if (S!= "") S = S+",";
|
||||
S += "0x" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16) + "LL";
|
||||
i++;
|
||||
r = r.shiftRight(64);
|
||||
}
|
||||
while (i<n64) {
|
||||
if (S!= "") S = S+",";
|
||||
S += "0LL";
|
||||
i++;
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
function toMontgomery(a) {
|
||||
return a.times(R).mod(self.header.P);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_buildFunctions(code) {
|
||||
for (let i=0; i<this.functions.length; i++) {
|
||||
const cfb = this.functions[i];
|
||||
cfb.build(code);
|
||||
}
|
||||
}
|
||||
|
||||
_buildComponents(code) {
|
||||
code.push("// Components");
|
||||
code.push(`Circom_Component _components[${this.components.length}] = {`);
|
||||
for (let i=0; i<this.components.length; i++) {
|
||||
const c = this.components[i];
|
||||
const sep = i>0 ? " ," : " ";
|
||||
code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildMapIsInput(code) {
|
||||
code.push("// mapIsInput");
|
||||
code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`);
|
||||
let line = "";
|
||||
for (let i=0; i<this.mapIsInput.length; i++) {
|
||||
line += i>0 ? ", " : " ";
|
||||
line += toHex(this.mapIsInput[i]);
|
||||
if (((i+1) % 64)==0) {
|
||||
code.push(" "+line);
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
if (line != "") code.push(" "+line);
|
||||
code.push("};");
|
||||
|
||||
function toHex(number) {
|
||||
if (number < 0) number = 0xFFFFFFFF + number + 1;
|
||||
let S=number.toString(16).toUpperCase();
|
||||
while (S.length<8) S = "0" + S;
|
||||
return "0x"+S;
|
||||
}
|
||||
}
|
||||
|
||||
_buildWit2Sig(code) {
|
||||
code.push("// Witness to Signal Table");
|
||||
code.push(`int _wit2sig[${this.wit2sig.length}] = {`);
|
||||
let line = "";
|
||||
for (let i=0; i<this.wit2sig.length; i++) {
|
||||
line += i>0 ? "," : " ";
|
||||
line += this.wit2sig[i];
|
||||
if (((i+1) % 64) == 0) {
|
||||
code.push(" "+line);
|
||||
line = "";
|
||||
}
|
||||
}
|
||||
if (line != "") code.push(" "+line);
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildCircuitVar(code) {
|
||||
|
||||
code.push(
|
||||
"// Circuit Variable",
|
||||
"Circom_Circuit _circuit = {" ,
|
||||
" NSignals,",
|
||||
" NComponents,",
|
||||
" NInputs,",
|
||||
" NOutputs,",
|
||||
" NVars,",
|
||||
" _wit2sig,",
|
||||
" _components,",
|
||||
" _mapIsInput,",
|
||||
" _constants,",
|
||||
" __P__",
|
||||
"};"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
const code=[];
|
||||
this._buildHeader(code);
|
||||
this._buildSizes(code);
|
||||
this._buildConstants(code);
|
||||
this._buildHashMaps(code);
|
||||
this._buildComponentEntriesTables(code);
|
||||
this._buildFunctions(code);
|
||||
this._buildComponents(code);
|
||||
this._buildMapIsInput(code);
|
||||
this._buildWit2Sig(code);
|
||||
this._buildCircuitVar(code);
|
||||
return streamFromMultiArray(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = BuilderC;
|
||||
206
ports/c/tester.js
Normal file
206
ports/c/tester.js
Normal file
@@ -0,0 +1,206 @@
|
||||
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 bigInt = require("big-integer");
|
||||
const utils = require("../../src/utils");
|
||||
const loadR1cs = require("r1csfile").load;
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
const buildZqField = require("ffiasm").buildZqField;
|
||||
|
||||
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.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
|
||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
||||
|
||||
options.p = options.p || bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
await compiler(circomFile, options);
|
||||
|
||||
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.h"), source.h, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.c"), source.c, "utf8");
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
await exec("nasm -fmacho64 --prefix _ " +
|
||||
` ${path.join(dir.path, "fr.asm")}`
|
||||
);
|
||||
} else if (process.platform === "linux") {
|
||||
await exec("nasm -felf64 " +
|
||||
` ${path.join(dir.path, "fr.asm")}`
|
||||
);
|
||||
} else throw("Unsupported platform");
|
||||
|
||||
const cdir = path.join(__dirname, "..", "..", "node_modules", "circom_runtime", "c");
|
||||
|
||||
await exec("g++" +
|
||||
` ${path.join(cdir, "main.cpp")}` +
|
||||
` ${path.join(cdir, "calcwit.cpp")}` +
|
||||
` ${path.join(cdir, "utils.cpp")}` +
|
||||
` ${path.join(dir.path, "fr.c")}` +
|
||||
` ${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(utils.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 = utils.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.field = 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 = bigInt(actualOut[self.symbols[prefix].varIdx]).toString();
|
||||
const be = bigInt(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.field;
|
||||
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.field;
|
||||
let v = F.zero;
|
||||
for (let w in lc) {
|
||||
v = F.add(
|
||||
v,
|
||||
F.mul( lc[w], witness[w] )
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
847
ports/wasm/build_runtime.js
Normal file
847
ports/wasm/build_runtime.js
Normal file
@@ -0,0 +1,847 @@
|
||||
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.i32_const(0),
|
||||
c.i32_const(0),
|
||||
c.i32_const(0),
|
||||
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 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.i32_add(
|
||||
c.i32_const(builder.pSignals),
|
||||
c.i32_mul(
|
||||
c.getLocal("i"),
|
||||
c.i32_const(builder.sizeFr)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
c.call(
|
||||
"Fr_toLongNormal",
|
||||
c.getLocal("pSrc")
|
||||
),
|
||||
|
||||
c.setLocal(
|
||||
"pDst",
|
||||
c.i32_add(
|
||||
c.i32_const(builder.pSignals),
|
||||
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.pSignals)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
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.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();
|
||||
|
||||
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");
|
||||
|
||||
};
|
||||
1023
ports/wasm/builder.js
Normal file
1023
ports/wasm/builder.js
Normal file
File diff suppressed because it is too large
Load Diff
10
ports/wasm/errs.js
Normal file
10
ports/wasm/errs.js
Normal file
@@ -0,0 +1,10 @@
|
||||
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"},
|
||||
};
|
||||
165
ports/wasm/tester.js
Normal file
165
ports/wasm/tester.js
Normal file
@@ -0,0 +1,165 @@
|
||||
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 bigInt = require("big-integer");
|
||||
const utils = require("../../src/utils");
|
||||
const loadR1cs = require("r1csfile").load;
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
|
||||
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.wasmWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".wasm"));
|
||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
||||
|
||||
const promisesArr = [];
|
||||
promisesArr.push(new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill)));
|
||||
|
||||
await compiler(circomFile, options);
|
||||
|
||||
await Promise.all(promisesArr);
|
||||
|
||||
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) {
|
||||
|
||||
return await this.witnessCalculator.calculateWitness(input);
|
||||
}
|
||||
|
||||
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.field = 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 = bigInt(actualOut[self.symbols[prefix].varIdx]).toString();
|
||||
const be = bigInt(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.field;
|
||||
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.field;
|
||||
let v = F.zero;
|
||||
for (let w in lc) {
|
||||
v = F.add(
|
||||
v,
|
||||
F.mul( lc[w], witness[w] )
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
59
src/bigarray.js
Normal file
59
src/bigarray.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const SUBARRAY_SIZE = 0x10000;
|
||||
|
||||
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 = [];
|
||||
|
||||
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 (element) {
|
||||
this.setElement (this.length, element);
|
||||
}
|
||||
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] = [];
|
||||
}
|
||||
const idx2 = idx % SUBARRAY_SIZE;
|
||||
this.arr[idx1][idx2] = value;
|
||||
if (idx >= this.length) this.length = idx+1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class BigArray {
|
||||
constructor( initSize ) {
|
||||
const obj = new _BigArray(initSize);
|
||||
const extObj = new Proxy(obj, BigArrayHandler);
|
||||
return extObj;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BigArray;
|
||||
462
src/build.js
Normal file
462
src/build.js
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
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 bigInt = require("big-integer");
|
||||
const utils = require("./utils");
|
||||
const gen = require("./gencode").gen;
|
||||
const createRefs = require("./gencode").createRefs;
|
||||
|
||||
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(bigInt.zero);
|
||||
ctx.addConstant(bigInt.one);
|
||||
|
||||
buildHeader(ctx);
|
||||
buildEntryTables(ctx);
|
||||
ctx.globalNames = ctx.uniqueNames;
|
||||
|
||||
buildCode(ctx);
|
||||
|
||||
buildComponentsArray(ctx);
|
||||
|
||||
buildMapIsInput(ctx);
|
||||
buildWit2Sig(ctx);
|
||||
|
||||
}
|
||||
|
||||
function buildEntryTables(ctx) {
|
||||
|
||||
const codes_hashMaps = [];
|
||||
const codes_componentEntries = [];
|
||||
const definedHashMaps = {};
|
||||
for (let i=0; i<ctx.components.length; i++) {
|
||||
const {htName, htMap} = addHashTable(i);
|
||||
|
||||
let code = "";
|
||||
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);
|
||||
|
||||
|
||||
code += `Circom_ComponentEntry ${componentEntriesTableName}[${htMap.length}] = {\n`;
|
||||
for (let j=0; j<htMap.length; j++) {
|
||||
const entry = ctx.components[i].names.o[htMap[j]];
|
||||
code += j>0 ? " ," : " ";
|
||||
const sizeName = ctx.addSizes(entry.sizes);
|
||||
|
||||
const ty = entry.type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code += `{${entry.offset},${sizeName}, ${ty}}\n`;
|
||||
}
|
||||
code += "};\n";
|
||||
codes_componentEntries.push(code);
|
||||
|
||||
ctx.components[i].htName = htName;
|
||||
ctx.components[i].etName = componentEntriesTableName;
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
"// HashMaps\n" ,
|
||||
codes_hashMaps , "\n" ,
|
||||
"\n" ,
|
||||
"// Component Entries\n" ,
|
||||
codes_componentEntries , "\n" ,
|
||||
"\n"
|
||||
];
|
||||
|
||||
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++) {
|
||||
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++) {
|
||||
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.field.p
|
||||
});
|
||||
}
|
||||
|
||||
function buildMapIsInput(ctx) {
|
||||
let i;
|
||||
let map = [];
|
||||
let acc = 0;
|
||||
for (i=0; i<ctx.signals.length; i++) {
|
||||
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 = Array(NVars);
|
||||
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>=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.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 + "=" + bigInt(ctx.signals[offset].value));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(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(v) {
|
||||
if (Array.isArray(v)) {
|
||||
let S="[";
|
||||
for (let i=0; i<v.length; i++) {
|
||||
if (i>0) S+=",";
|
||||
S+=value2str(v[i]);
|
||||
}
|
||||
S+="]";
|
||||
return S;
|
||||
} else {
|
||||
return bigInt(v).toString();
|
||||
}
|
||||
}
|
||||
158
src/buildsyms.js
Normal file
158
src/buildsyms.js
Normal file
@@ -0,0 +1,158 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
488
src/compiler.js
488
src/compiler.js
@@ -17,285 +17,393 @@
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const bigInt = require("big-integer");
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const __MASK__ = new bigInt(2).pow(253).minus(1);
|
||||
const assert = require("assert");
|
||||
const gen = require("./gencode");
|
||||
const exec = require("./exec");
|
||||
const lc = require("./lcalgebra");
|
||||
const __P__ = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const sONE = 0;
|
||||
const build = require("./build");
|
||||
const BuilderC = require("../ports/c/builder.js");
|
||||
const BuilderWasm = require("../ports/wasm/builder.js");
|
||||
const constructionPhase = require("./construction_phase");
|
||||
const Ctx = require("./ctx");
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
const utils = require("./utils");
|
||||
const buildR1cs = require("./r1csfile").buildR1cs;
|
||||
const BigArray = require("./bigarray");
|
||||
const buildSyms = require("./buildsyms");
|
||||
|
||||
module.exports = compile;
|
||||
|
||||
const parser = require("../parser/jaz.js").parser;
|
||||
async function compile(srcFile, options) {
|
||||
options.p = options.p || __P__;
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (typeof options.reduceConstraints === "undefined") {
|
||||
options.reduceConstraints = true;
|
||||
}
|
||||
const ctx = new Ctx();
|
||||
ctx.field = new ZqField(options.p);
|
||||
ctx.verbose= options.verbose || false;
|
||||
ctx.mainComponent = options.mainComponent || "main";
|
||||
ctx.newThreadTemplates = options.newThreadTemplates;
|
||||
|
||||
const timeout = ms => new Promise(res => setTimeout(res, ms));
|
||||
constructionPhase(ctx, srcFile);
|
||||
|
||||
async function compile(srcFile) {
|
||||
const fullFileName = srcFile;
|
||||
const fullFilePath = path.dirname(fullFileName);
|
||||
if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length);
|
||||
|
||||
const src = fs.readFileSync(fullFileName, "utf8");
|
||||
const ast = parser.parse(src);
|
||||
if (ctx.error) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
|
||||
assert(ast.type == "BLOCK");
|
||||
|
||||
const ctx = {
|
||||
scopes: [{}],
|
||||
signals: {
|
||||
one: {
|
||||
fullName: "one",
|
||||
value: bigInt(1),
|
||||
equivalence: "",
|
||||
direction: ""
|
||||
}
|
||||
},
|
||||
currentComponent: "",
|
||||
constraints: [],
|
||||
components: {},
|
||||
templates: {},
|
||||
functions: {},
|
||||
functionParams: {},
|
||||
filePath: fullFilePath,
|
||||
fileName: fullFileName
|
||||
};
|
||||
|
||||
|
||||
exec(ctx, ast);
|
||||
|
||||
if (!ctx.components["main"]) {
|
||||
if (ctx.getComponentIdx(ctx.mainComponent)<0) {
|
||||
throw new Error("A main component must be defined");
|
||||
}
|
||||
|
||||
if (ctx.verbose) console.log("Classify Signals");
|
||||
classifySignals(ctx);
|
||||
reduceConstants(ctx);
|
||||
|
||||
// Repeat while reductions are performed
|
||||
let oldNConstrains = -1;
|
||||
while (ctx.constraints.length != oldNConstrains) {
|
||||
oldNConstrains = ctx.constraints.length;
|
||||
reduceConstrains(ctx);
|
||||
if (ctx.verbose) console.log("Reduce Constants");
|
||||
reduceConstants(ctx);
|
||||
if (options.reduceConstraints) {
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constraints");
|
||||
// Repeat while reductions are performed
|
||||
let oldNConstrains = -1;
|
||||
while (ctx.constraints.length != oldNConstrains) {
|
||||
if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length);
|
||||
oldNConstrains = ctx.constraints.length;
|
||||
reduceConstrains(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
|
||||
|
||||
generateWitnessNames(ctx);
|
||||
|
||||
if (ctx.error) {
|
||||
throw(ctx.error);
|
||||
}
|
||||
|
||||
ctx.scopes = [{}];
|
||||
if (options.cSourceWriteStream) {
|
||||
ctx.builder = new BuilderC();
|
||||
build(ctx);
|
||||
const rdStream = ctx.builder.build();
|
||||
rdStream.pipe(options.cSourceWriteStream);
|
||||
|
||||
const mainCode = gen(ctx,ast);
|
||||
// await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
|
||||
}
|
||||
|
||||
if ((options.wasmWriteStream)||(options.watWriteStream)) {
|
||||
ctx.builder = new BuilderWasm();
|
||||
build(ctx);
|
||||
if (options.wasmWriteStream) {
|
||||
const rdStream = ctx.builder.build("wasm");
|
||||
rdStream.pipe(options.wasmWriteStream);
|
||||
}
|
||||
if (options.watWriteStream) {
|
||||
const rdStream = ctx.builder.build("wat");
|
||||
rdStream.pipe(options.watWriteStream);
|
||||
}
|
||||
|
||||
// await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
|
||||
}
|
||||
|
||||
// const mainCode = gen(ctx,ast);
|
||||
if (ctx.error) throw(ctx.error);
|
||||
|
||||
const def = buildCircuitDef(ctx, mainCode);
|
||||
if (options.r1csFileName) {
|
||||
await buildR1cs(ctx, options.r1csFileName);
|
||||
}
|
||||
|
||||
if (options.symWriteStream) {
|
||||
const rdStream = buildSyms(ctx);
|
||||
rdStream.pipe(options.symWriteStream);
|
||||
|
||||
// await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
|
||||
}
|
||||
|
||||
// const def = buildCircuitDef(ctx, mainCode);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function classifySignals(ctx) {
|
||||
|
||||
const ERROR = 0xFFFF;
|
||||
|
||||
function priorize(t1, t2) {
|
||||
if ((t1 == "error") || (t2=="error")) return "error";
|
||||
if (t1 == "internal") {
|
||||
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
||||
if (t1 == ctx.stINTERNAL) {
|
||||
return t2;
|
||||
} else if (t2=="internal") {
|
||||
} else if (t2==ctx.stINTERNAL) {
|
||||
return t1;
|
||||
}
|
||||
if ((t1 == "one") || (t2 == "one")) return "one";
|
||||
if ((t1 == "constant") || (t2 == "constant")) return "constant";
|
||||
if (t1!=t2) return "error";
|
||||
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
||||
if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
|
||||
if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
|
||||
if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
|
||||
if (t1!=t2) return ERROR;
|
||||
return t1;
|
||||
}
|
||||
|
||||
// First classify the signals
|
||||
for (let s in ctx.signals) {
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
const signal = ctx.signals[s];
|
||||
let tAll = "internal";
|
||||
let tAll = ctx.stINTERNAL;
|
||||
let lSignal = signal;
|
||||
let end = false;
|
||||
while (!end) {
|
||||
let t = lSignal.category || "internal";
|
||||
if (s == "one") {
|
||||
t = "one";
|
||||
} else if (lSignal.value) {
|
||||
t = "constant";
|
||||
} else if (lSignal.component=="main") {
|
||||
if (lSignal.direction == "IN") {
|
||||
if (lSignal.private) {
|
||||
t = "prvInput";
|
||||
let t = lSignal.c || ctx.stINTERNAL;
|
||||
if (s == 0) {
|
||||
t = ctx.stONE;
|
||||
} else if (lSignal.o & ctx.MAIN) {
|
||||
if (lSignal.o & ctx.IN) {
|
||||
if (lSignal.o & ctx.PRV) {
|
||||
t = ctx.stPRVINPUT;
|
||||
} else {
|
||||
t = "pubInput";
|
||||
t = ctx.stPUBINPUT;
|
||||
}
|
||||
} else if (lSignal.direction == "OUT") {
|
||||
t = "output";
|
||||
} else if (lSignal.o & ctx.OUT) {
|
||||
t = ctx.stOUTPUT;
|
||||
}
|
||||
} else if (utils.isDefined(lSignal.v)) {
|
||||
t = ctx.stCONSTANT;
|
||||
}
|
||||
tAll = priorize(t,tAll);
|
||||
if (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
if (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
} else {
|
||||
end=true;
|
||||
}
|
||||
}
|
||||
if (tAll == "error") {
|
||||
if (tAll == ERROR) {
|
||||
throw new Error("Incompatible types in signal: " + s);
|
||||
}
|
||||
lSignal.category = tAll;
|
||||
lSignal.c = tAll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function generateWitnessNames(ctx) {
|
||||
|
||||
const totals = {
|
||||
"output": 0,
|
||||
"pubInput": 0,
|
||||
"one": 0,
|
||||
"prvInput": 0,
|
||||
"internal": 0,
|
||||
"constant": 0,
|
||||
};
|
||||
const totals = {};
|
||||
totals[ctx.stONE] = 0;
|
||||
totals[ctx.stOUTPUT] = 0;
|
||||
totals[ctx.stPUBINPUT] = 0;
|
||||
totals[ctx.stPRVINPUT] = 0;
|
||||
totals[ctx.stINTERNAL] = 0;
|
||||
totals[ctx.stDISCARDED] = 0;
|
||||
totals[ctx.stCONSTANT] = 0;
|
||||
const ids = {};
|
||||
|
||||
const counted = {};
|
||||
|
||||
// First classify the signals
|
||||
for (let s in ctx.signals) {
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
|
||||
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.equivalence) lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
|
||||
|
||||
if (!counted[lSignal.fullName]) {
|
||||
counted[lSignal.fullName] = true;
|
||||
totals[lSignal.category] ++;
|
||||
if (!( lSignal.o & ctx.COUNTED) ) {
|
||||
lSignal.o |= ctx.COUNTED;
|
||||
totals[lSignal.c] ++;
|
||||
}
|
||||
}
|
||||
|
||||
ids["one"] = 0;
|
||||
ids["output"] = 1;
|
||||
ids["pubInput"] = ids["output"] + totals["output"];
|
||||
ids["prvInput"] = ids["pubInput"] + totals["pubInput"];
|
||||
ids["internal"] = ids["prvInput"] + totals["prvInput"];
|
||||
ids["constant"] = ids["internal"] + totals["internal"];
|
||||
const nSignals = ids["constant"] + totals["constant"];
|
||||
ids[ctx.stONE] = 0;
|
||||
ids[ctx.stOUTPUT] = 1;
|
||||
ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
|
||||
ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
|
||||
ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
|
||||
ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
|
||||
ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
|
||||
const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
|
||||
|
||||
ctx.signalNames = new Array(nSignals);
|
||||
for (let i=0; i< nSignals; i++) ctx.signalNames[i] = [];
|
||||
ctx.signalName2Idx = {};
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
|
||||
if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
|
||||
|
||||
for (let s in ctx.signals) {
|
||||
const signal = ctx.signals[s];
|
||||
let lSignal = signal;
|
||||
while (lSignal.equivalence) {
|
||||
lSignal = ctx.signals[lSignal.equivalence];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if ( typeof(lSignal.id) === "undefined" ) {
|
||||
lSignal.id = ids[lSignal.category] ++;
|
||||
lSignal.id = ids[lSignal.c] ++;
|
||||
}
|
||||
|
||||
signal.id = lSignal.id;
|
||||
ctx.signalNames[signal.id].push(signal.fullName);
|
||||
ctx.signalName2Idx[signal.fullName] = signal.id;
|
||||
}
|
||||
|
||||
ctx.totals = totals;
|
||||
}
|
||||
|
||||
function reduceConstants(ctx) {
|
||||
const newConstraints = [];
|
||||
const newConstraints = new BigArray();
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
const c = lc.canonize(ctx, ctx.constraints[i]);
|
||||
if (!lc.isZero(c)) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
|
||||
const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
|
||||
if (!ctx.lc.isZero(c)) {
|
||||
newConstraints.push(c);
|
||||
}
|
||||
delete ctx.constraints[i];
|
||||
}
|
||||
ctx.constraints = newConstraints;
|
||||
}
|
||||
|
||||
function reduceConstrains(ctx) {
|
||||
const newConstraints = [];
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
const c = ctx.constraints[i];
|
||||
indexVariables();
|
||||
let possibleConstraints = ctx.constraints;
|
||||
let ii=0;
|
||||
while (possibleConstraints.length>0) {
|
||||
let nextPossibleConstraints = new BigArray();
|
||||
for (let i=0; i<possibleConstraints.length; i++) {
|
||||
ii++;
|
||||
if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
|
||||
if (!ctx.constraints[i]) continue;
|
||||
const c = ctx.constraints[i];
|
||||
|
||||
// Swap a and b if b has more variables.
|
||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||
const aux = c.a;
|
||||
c.a=c.b;
|
||||
c.b=aux;
|
||||
}
|
||||
// Swap a and b if b has more variables.
|
||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||
const aux = c.a;
|
||||
c.a=c.b;
|
||||
c.b=aux;
|
||||
}
|
||||
|
||||
// Mov to C if possible.
|
||||
if (isConstant(c.a)) {
|
||||
const ct = {type: "NUMBER", value: c.a.values["one"]};
|
||||
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
}
|
||||
if (isConstant(c.b)) {
|
||||
const ct = {type: "NUMBER", value: c.b.values["one"]};
|
||||
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
||||
}
|
||||
// Mov to C if possible.
|
||||
if (isConstant(c.a)) {
|
||||
const ct = {t: "N", v: c.a.coefs[sONE]};
|
||||
c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
|
||||
c.a = { t: "LC", coefs: {} };
|
||||
c.b = { t: "LC", coefs: {} };
|
||||
}
|
||||
if (isConstant(c.b)) {
|
||||
const ct = {t: "N", v: c.b.coefs[sONE]};
|
||||
c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
|
||||
c.a = { t: "LC", coefs: {} };
|
||||
c.b = { t: "LC", coefs: {} };
|
||||
}
|
||||
|
||||
if (lc.isZero(c.a) || lc.isZero(c.b)) {
|
||||
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||
if (isolatedSignal) {
|
||||
const isolatedSignalEquivalence = {
|
||||
type: "LINEARCOMBINATION",
|
||||
values: {}
|
||||
};
|
||||
const invCoef = c.c.values[isolatedSignal].modInv(__P__);
|
||||
for (const s in c.c.values) {
|
||||
if (s != isolatedSignal) {
|
||||
const v = __P__.minus(c.c.values[s]).times(invCoef).mod(__P__);
|
||||
if (!v.isZero()) {
|
||||
isolatedSignalEquivalence.values[s] = v;
|
||||
if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
|
||||
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||
if (isolatedSignal) {
|
||||
|
||||
let lSignal = ctx.signals[isolatedSignal];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
|
||||
|
||||
const isolatedSignalEquivalence = {
|
||||
t: "LC",
|
||||
coefs: {}
|
||||
};
|
||||
const invCoef = c.c.coefs[isolatedSignal].modInv(__P__);
|
||||
for (const s in c.c.coefs) {
|
||||
if (s != isolatedSignal) {
|
||||
const v = __P__.minus(c.c.coefs[s]).times(invCoef).mod(__P__);
|
||||
if (!v.isZero()) {
|
||||
isolatedSignalEquivalence.coefs[s] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let j=0; j<newConstraints.length; j++) {
|
||||
newConstraints[j] = lc.substitute(newConstraints[j], isolatedSignal, isolatedSignalEquivalence);
|
||||
for (let j in lSignal.inConstraints) {
|
||||
if ((j!=i)&&(ctx.constraints[j])) {
|
||||
ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
|
||||
linkSignalsConstraint(j);
|
||||
if (j<i) {
|
||||
nextPossibleConstraints.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.constraints[i] = null;
|
||||
|
||||
lSignal.c = ctx.stDISCARDED;
|
||||
} else {
|
||||
if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
|
||||
}
|
||||
for (let j=i+1; j<ctx.constraints.length; j++ ) {
|
||||
ctx.constraints[j] = lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
|
||||
}
|
||||
c.a={ type: "LINEARCOMBINATION", values: {} };
|
||||
c.b={ type: "LINEARCOMBINATION", values: {} };
|
||||
c.c={ type: "LINEARCOMBINATION", values: {} };
|
||||
isolatedSignal.category = "constant";
|
||||
}
|
||||
}
|
||||
possibleConstraints = nextPossibleConstraints;
|
||||
}
|
||||
unindexVariables();
|
||||
|
||||
if (!lc.isZero(c)) {
|
||||
newConstraints.push(c);
|
||||
// Pack the constraints
|
||||
let o = 0;
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if (ctx.constraints[i]) {
|
||||
if (o != i) {
|
||||
ctx.constraints[o] = ctx.constraints[i];
|
||||
}
|
||||
o++;
|
||||
}
|
||||
}
|
||||
ctx.constraints = newConstraints;
|
||||
ctx.constraints.length = o;
|
||||
|
||||
function indexVariables() {
|
||||
for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
|
||||
}
|
||||
|
||||
function linkSignalsConstraint(cidx) {
|
||||
const ct = ctx.constraints[cidx];
|
||||
for (let k in ct.a.coefs) linkSignal(k, cidx);
|
||||
for (let k in ct.b.coefs) linkSignal(k, cidx);
|
||||
for (let k in ct.c.coefs) linkSignal(k, cidx);
|
||||
}
|
||||
|
||||
function unindexVariables() {
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
let lSignal = ctx.signals[s];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (lSignal.inConstraints) delete lSignal.inConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function unlinkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
|
||||
delete lSignal.inConstraints[cidx];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function linkSignal(signalName, cidx) {
|
||||
let lSignal = ctx.signals[signalName];
|
||||
while (lSignal.e>=0) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
if (!lSignal.inConstraints) lSignal.inConstraints = {};
|
||||
lSignal.inConstraints[cidx] = true;
|
||||
}
|
||||
|
||||
function getFirstInternalSignal(ctx, l) {
|
||||
for (let k in l.values) {
|
||||
for (let k in l.coefs) {
|
||||
const signal = ctx.signals[k];
|
||||
if (signal.category == "internal") return k;
|
||||
if (signal.c == ctx.stINTERNAL) return k;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isConstant(l) {
|
||||
for (let k in l.values) {
|
||||
if ((k != "one") && (!l.values[k].isZero())) return false;
|
||||
for (let k in l.coefs) {
|
||||
if ((k != sONE) && (!l.coefs[k].isZero())) return false;
|
||||
}
|
||||
if (!l.values["one"] || l.values["one"].isZero()) return false;
|
||||
if (!l.coefs[sONE] || l.coefs[sONE].isZero()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
function buildCircuitDef(ctx, mainCode) {
|
||||
const res = {
|
||||
@@ -354,6 +462,9 @@ function buildCircuitDef(ctx, mainCode) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Build constraints
|
||||
|
||||
@@ -375,14 +486,14 @@ is converted to
|
||||
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();
|
||||
if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
|
||||
for (let s in src.coefs) {
|
||||
const v = src.coefs[s].toString();
|
||||
const id = ctx.signalName2Idx[s];
|
||||
dst[id] = v;
|
||||
}
|
||||
@@ -395,13 +506,54 @@ function buildConstraints(ctx) {
|
||||
|
||||
fillLC(A, ctx.constraints[i].a);
|
||||
fillLC(B, ctx.constraints[i].b);
|
||||
fillLC(C, lc.negate(ctx.constraints[i].c));
|
||||
fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
|
||||
|
||||
res.push([A,B,C]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
function buildSyms(ctx, strm) {
|
||||
|
||||
let nSyms;
|
||||
|
||||
addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
|
||||
|
||||
|
||||
function addSymbolsComponent(prefix, idComponet) {
|
||||
for (let n in ctx.components[idComponet].names.o) {
|
||||
const entrie = ctx.components[idComponet].names.o[n];
|
||||
addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
|
||||
}
|
||||
}
|
||||
|
||||
function addSymbolArray(prefix, type, sizes, offset) {
|
||||
if (sizes.length==0) {
|
||||
if (type == "S") {
|
||||
let s=offset;
|
||||
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
|
||||
let wId = ctx.signals[s].id;
|
||||
if (typeof(wId) == "undefined") wId=-1;
|
||||
strm.write(`${offset},${wId},${prefix}\n`);
|
||||
nSyms ++;
|
||||
if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
|
||||
} else {
|
||||
addSymbolsComponent(prefix+".", offset);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
let acc = 0;
|
||||
for (let i=0; i<sizes[0]; i++) {
|
||||
acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
1076
src/construction_phase.js
Normal file
1076
src/construction_phase.js
Normal file
File diff suppressed because it is too large
Load Diff
227
src/ctx.js
Normal file
227
src/ctx.js
Normal file
@@ -0,0 +1,227 @@
|
||||
const bigInt = require("big-integer");
|
||||
const BigArray = require("./bigarray.js");
|
||||
|
||||
|
||||
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() {
|
||||
|
||||
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: bigInt(1),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
1109
src/exec.js
1109
src/exec.js
File diff suppressed because it is too large
Load Diff
@@ -1,70 +0,0 @@
|
||||
|
||||
|
||||
module.exports = genOpt;
|
||||
|
||||
|
||||
function genOpt(ctx, ast) {
|
||||
if (ast.type == "OP") {
|
||||
if (ast.op == "=") {
|
||||
return genOptVarAssignement(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "GENOPT -> Invalid operation: " + ast.op);
|
||||
}
|
||||
} else if (ast.type == "TEMPLATEDEF") {
|
||||
return genOptTemplateDef(ctx, ast);
|
||||
} else {
|
||||
error(ctx, ast, "GENOPT -> 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 genOptTemplateDef(ctx, ast) {
|
||||
if (ctx.templates[ast.name]) {
|
||||
return error(ctx, ast, "Template name already exists: "+ast.name);
|
||||
}
|
||||
ctx.templates[ast.name] = {
|
||||
type: "TEMPLATE",
|
||||
params: ast.params,
|
||||
block: ast.block,
|
||||
fileName: ctx.fileName,
|
||||
filePath: ctx.filePath
|
||||
};
|
||||
}
|
||||
|
||||
function genOptVarAssignement(ctx, ast) {
|
||||
let varName;
|
||||
if (ast.values[0].type == "DECLARE") {
|
||||
varName = genOptCode(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
} else {
|
||||
varName = ast.values[0];
|
||||
}
|
||||
const varContent = getScope(ctx, varName.name, varName.selectors);
|
||||
if (ctx.error) return;
|
||||
|
||||
if ((typeof(varContent) != "object")||(varContent == null)) return error(ctx, ast, "Variable not defined");
|
||||
|
||||
if (varContent.type == "COMPONENT") return genOptInstantiateComponet(ctx, varName, ast.values[1]);
|
||||
if (varContent.type == "SIGNAL") return error(ctx, ast, "Cannot assig to a signal with `=` use <-- or <== ops");
|
||||
|
||||
const res = genOpt(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
setScope(ctx, varName.name, varName.selectors, res);
|
||||
|
||||
return v;
|
||||
}
|
||||
1603
src/gencode.js
1603
src/gencode.js
File diff suppressed because it is too large
Load Diff
75
src/iterateast.js
Normal file
75
src/iterateast.js
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
972
src/lcalgebra.js
972
src/lcalgebra.js
File diff suppressed because it is too large
Load Diff
146
src/r1csfile.js
Normal file
146
src/r1csfile.js
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const assert = require("assert");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
module.exports.buildR1cs = buildR1cs;
|
||||
|
||||
async function buildR1cs(ctx, fileName) {
|
||||
|
||||
const fd = await fs.promises.open(fileName, "w");
|
||||
|
||||
|
||||
await fd.write("r1cs"); // Magic "r1cs"
|
||||
|
||||
let p = 4;
|
||||
await writeU32(1); // Version
|
||||
await writeU32(3); // Number of Sections
|
||||
|
||||
// Write the header
|
||||
///////////
|
||||
await writeU32(1); // Header type
|
||||
const pHeaderSize = p;
|
||||
await writeU64(0); // Temporally set to 0 length
|
||||
|
||||
|
||||
const n8 = (Math.floor( (ctx.field.p.bitLength() - 1) / 64) +1)*8;
|
||||
// Field Def
|
||||
await writeU32(n8); // Temporally set to 0 length
|
||||
await writeBigInt(ctx.field.p);
|
||||
|
||||
const NWires =
|
||||
ctx.totals[ctx.stONE] +
|
||||
ctx.totals[ctx.stOUTPUT] +
|
||||
ctx.totals[ctx.stPUBINPUT] +
|
||||
ctx.totals[ctx.stPRVINPUT] +
|
||||
ctx.totals[ctx.stINTERNAL];
|
||||
|
||||
await writeU32(NWires);
|
||||
await writeU32(ctx.totals[ctx.stOUTPUT]);
|
||||
await writeU32(ctx.totals[ctx.stPUBINPUT]);
|
||||
await writeU32(ctx.totals[ctx.stPRVINPUT]);
|
||||
await writeU64(ctx.signals.length);
|
||||
await writeU32(ctx.constraints.length);
|
||||
|
||||
const headerSize = p - pHeaderSize - 8;
|
||||
|
||||
// Write constraints
|
||||
///////////
|
||||
await writeU32(2); // Constraints type
|
||||
const pConstraintsSize = p;
|
||||
await writeU64(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 fd.datasync();
|
||||
}
|
||||
await writeConstraint(ctx.constraints[i]);
|
||||
}
|
||||
|
||||
const constraintsSize = p - pConstraintsSize - 8;
|
||||
|
||||
// Write map
|
||||
///////////
|
||||
await writeU32(3); // wires2label type
|
||||
const pMapSize = p;
|
||||
await writeU64(0); // Temporally set to 0 length
|
||||
|
||||
|
||||
const arr = new Array(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 writeU64(arr[i]);
|
||||
if ((ctx.verbose)&&(i%100000)) console.log("writing wire2label map: ", i);
|
||||
}
|
||||
|
||||
const mapSize = p - pMapSize - 8;
|
||||
|
||||
// Write sizes
|
||||
await writeU32(headerSize, pHeaderSize);
|
||||
await writeU32(constraintsSize, pConstraintsSize);
|
||||
await writeU32(mapSize, pMapSize);
|
||||
|
||||
await fd.sync();
|
||||
await fd.close();
|
||||
|
||||
async function writeU32(v, pos) {
|
||||
const b = Buffer.allocUnsafe(4);
|
||||
b.writeInt32LE(v);
|
||||
|
||||
await fd.write(b, 0, 4, pos);
|
||||
|
||||
if (typeof(pos) == "undefined") p += 4;
|
||||
}
|
||||
|
||||
async function writeU64(v, pos) {
|
||||
const b = Buffer.allocUnsafe(8);
|
||||
b.writeBigUInt64LE(BigInt(v));
|
||||
|
||||
await fd.write(b, 0, 8, pos);
|
||||
|
||||
if (typeof(pos) == "undefined") p += 8;
|
||||
}
|
||||
|
||||
async function writeConstraint(c) {
|
||||
await writeLC(c.a);
|
||||
await writeLC(c.b);
|
||||
await writeLC(ctx.lc.neg(c.c));
|
||||
}
|
||||
|
||||
async function writeLC(lc) {
|
||||
const idxs = Object.keys(lc.coefs);
|
||||
await writeU32(idxs.length);
|
||||
for (let s in lc.coefs) {
|
||||
let lSignal = ctx.signals[s];
|
||||
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
|
||||
await writeU32(lSignal.id);
|
||||
await writeBigInt(lc.coefs[s]);
|
||||
}
|
||||
}
|
||||
|
||||
async function writeBigInt(n, pos) {
|
||||
const b = Buffer.allocUnsafe(n8);
|
||||
|
||||
const dwords = bigInt(n).toArray(0x100000000).value;
|
||||
|
||||
for (let i=0; i<dwords.length; i++) {
|
||||
b.writeUInt32LE(dwords[dwords.length-1-i], i*4, 4 );
|
||||
}
|
||||
b.fill(0, dwords.length*4);
|
||||
|
||||
await fd.write(b, 0, fs, pos);
|
||||
|
||||
if (typeof(pos) == "undefined") p += n8;
|
||||
}
|
||||
}
|
||||
21
src/streamfromarray_bin.js
Normal file
21
src/streamfromarray_bin.js
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
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;
|
||||
};
|
||||
52
src/streamfromarray_txt.js
Normal file
52
src/streamfromarray_txt.js
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
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 (!Array.isArray(ma)) 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));
|
||||
}
|
||||
};
|
||||
134
src/utils.js
Normal file
134
src/utils.js
Normal file
@@ -0,0 +1,134 @@
|
||||
const fnv = require("fnv-plus");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
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.stringifyBigInts = stringifyBigInts;
|
||||
module.exports.unstringifyBigInts = unstringifyBigInts;
|
||||
module.exports.sameSizes = sameSizes;
|
||||
module.exports.isDefined = isDefined;
|
||||
module.exports.accSizes2Str = accSizes2Str;
|
||||
|
||||
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(bigInt(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 stringifyBigInts(o) {
|
||||
if ((typeof(o) == "bigint") || o.isZero !== undefined) {
|
||||
return o.toString(10);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(stringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
res[k] = stringifyBigInts(o[k]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function unstringifyBigInts(o) {
|
||||
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
|
||||
return bigInt(o);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return bigInt(o);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
65
test/basiccases.js
Normal file
65
test/basiccases.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const path = require("path");
|
||||
|
||||
const bigInt = require("big-integer");
|
||||
const c_tester = require("../index.js").c_tester;
|
||||
const wasm_tester = require("../index.js").wasm_tester;
|
||||
|
||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
const basicCases = require("./basiccases.json");
|
||||
|
||||
function normalize(o) {
|
||||
if ((typeof(o) == "bigint") || o.isZero !== undefined) {
|
||||
const res = bigInt(o);
|
||||
return norm(res);
|
||||
} 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 {
|
||||
const res = bigInt(o);
|
||||
return norm(res);
|
||||
}
|
||||
|
||||
function norm(n) {
|
||||
let res = n.mod(__P__);
|
||||
if (res.isNegative()) res = __P__.add(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
307
test/basiccases.json
Normal file
307
test/basiccases.json
Normal file
@@ -0,0 +1,307 @@
|
||||
[
|
||||
{
|
||||
"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,52 +0,0 @@
|
||||
const chai = require("chai");
|
||||
const path = require("path");
|
||||
const snarkjs = require("snarkjs");
|
||||
|
||||
const bigInt = snarkjs.bigInt;
|
||||
|
||||
const compiler = require("../index.js");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
async function assertThrowsAsync(fn, regExp) {
|
||||
let f = () => {};
|
||||
try {
|
||||
await fn();
|
||||
} catch(e) {
|
||||
f = () => { throw e; };
|
||||
} finally {
|
||||
assert.throws(f, regExp);
|
||||
}
|
||||
}
|
||||
|
||||
describe("Sum test", () => {
|
||||
it("Should compile a code with an undefined if", async () => {
|
||||
await compiler(path.join(__dirname, "circuits", "undefinedif.circom"));
|
||||
});
|
||||
it("Should compile a code with vars inside a for", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "forvariables.circom"));
|
||||
|
||||
const circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
const witness = circuit.calculateWitness({ "in": 111});
|
||||
assert(witness[0].equals(bigInt(1)));
|
||||
assert(witness[1].equals(bigInt(114)));
|
||||
assert(witness[2].equals(bigInt(111)));
|
||||
|
||||
});
|
||||
it("Should compile a code with an undefined if", async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "mixvarsignal.circom"));
|
||||
|
||||
const circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
const witness = circuit.calculateWitness({ "i": 111});
|
||||
assert(witness[0].equals(bigInt(1)));
|
||||
assert(witness[1].equals(bigInt(111)));
|
||||
assert(witness[2].equals(bigInt(111)));
|
||||
});
|
||||
// it("Should assign signal ERROR", async () => {
|
||||
// await assertThrowsAsync(async () => {
|
||||
// await compiler(path.join(__dirname, "circuits", "assignsignal.circom"));
|
||||
// }, /Cannot assign to a signal .*/);
|
||||
// });
|
||||
});
|
||||
9
test/circuits/add.circom
Normal file
9
test/circuits/add.circom
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
template Add() {
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
out <== in[0] + in[1];
|
||||
}
|
||||
|
||||
component main = Add();
|
||||
16
test/circuits/addconst1.circom
Normal file
16
test/circuits/addconst1.circom
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
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);
|
||||
42
test/circuits/arrays.circom
Normal file
42
test/circuits/arrays.circom
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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();
|
||||
28
test/circuits/componentarray.circom
Normal file
28
test/circuits/componentarray.circom
Normal file
@@ -0,0 +1,28 @@
|
||||
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);
|
||||
27
test/circuits/componentarray2.circom
Normal file
27
test/circuits/componentarray2.circom
Normal file
@@ -0,0 +1,27 @@
|
||||
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);
|
||||
17
test/circuits/compute.circom
Normal file
17
test/circuits/compute.circom
Normal file
@@ -0,0 +1,17 @@
|
||||
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();
|
||||
15
test/circuits/condternary.circom
Normal file
15
test/circuits/condternary.circom
Normal file
@@ -0,0 +1,15 @@
|
||||
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()
|
||||
17
test/circuits/constantcircuit.circom
Normal file
17
test/circuits/constantcircuit.circom
Normal file
@@ -0,0 +1,17 @@
|
||||
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);
|
||||
18
test/circuits/constantinternalcircuit.circom
Normal file
18
test/circuits/constantinternalcircuit.circom
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
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();
|
||||
39
test/circuits/constants1.circom
Normal file
39
test/circuits/constants1.circom
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
|
||||
|
||||
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();
|
||||
23
test/circuits/dec.circom
Normal file
23
test/circuits/dec.circom
Normal file
@@ -0,0 +1,23 @@
|
||||
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();
|
||||
14
test/circuits/forrolled.circom
Normal file
14
test/circuits/forrolled.circom
Normal file
@@ -0,0 +1,14 @@
|
||||
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();
|
||||
10
test/circuits/forunrolled.circom
Normal file
10
test/circuits/forunrolled.circom
Normal file
@@ -0,0 +1,10 @@
|
||||
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);
|
||||
12
test/circuits/function1.circom
Normal file
12
test/circuits/function1.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
function func1(a,b) {
|
||||
return a+b;
|
||||
}
|
||||
|
||||
template Main() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== func1(in, 3);
|
||||
}
|
||||
|
||||
component main = Main();
|
||||
13
test/circuits/function2.circom
Normal file
13
test/circuits/function2.circom
Normal file
@@ -0,0 +1,13 @@
|
||||
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();
|
||||
26
test/circuits/ifrolled.circom
Normal file
26
test/circuits/ifrolled.circom
Normal file
@@ -0,0 +1,26 @@
|
||||
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();
|
||||
31
test/circuits/ifunrolled.circom
Normal file
31
test/circuits/ifunrolled.circom
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
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
test/circuits/in.bin
Normal file
1
test/circuits/in.bin
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
test/circuits/in.json
Normal file
1
test/circuits/in.json
Normal file
@@ -0,0 +1 @@
|
||||
{"in1": 1, "in2": [2,3], "in3":[[4,5], [6,7], [8,9]]}
|
||||
24
test/circuits/inc.circom
Normal file
24
test/circuits/inc.circom
Normal file
@@ -0,0 +1,24 @@
|
||||
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();
|
||||
|
||||
16
test/circuits/include.circom
Normal file
16
test/circuits/include.circom
Normal file
@@ -0,0 +1,16 @@
|
||||
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();
|
||||
10
test/circuits/included.circom
Normal file
10
test/circuits/included.circom
Normal file
@@ -0,0 +1,10 @@
|
||||
template T1() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
out <== in**2/3;
|
||||
}
|
||||
|
||||
function F1(a) {
|
||||
return a**2/3;
|
||||
}
|
||||
54
test/circuits/inout.circom
Normal file
54
test/circuits/inout.circom
Normal file
@@ -0,0 +1,54 @@
|
||||
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();
|
||||
@@ -8,7 +8,7 @@ template X() {
|
||||
}
|
||||
|
||||
i === r;
|
||||
out <== r;
|
||||
out <== i*i;
|
||||
}
|
||||
|
||||
component main = X();
|
||||
|
||||
12
test/circuits/ops.circom
Normal file
12
test/circuits/ops.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
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();
|
||||
12
test/circuits/ops2.circom
Normal file
12
test/circuits/ops2.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
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();
|
||||
12
test/circuits/ops3.circom
Normal file
12
test/circuits/ops3.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
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();
|
||||
18
test/circuits/opsbit.circom
Normal file
18
test/circuits/opsbit.circom
Normal file
@@ -0,0 +1,18 @@
|
||||
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();
|
||||
18
test/circuits/opscmp.circom
Normal file
18
test/circuits/opscmp.circom
Normal file
@@ -0,0 +1,18 @@
|
||||
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();
|
||||
12
test/circuits/opslog.circom
Normal file
12
test/circuits/opslog.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
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();
|
||||
16
test/circuits/whilerolled.circom
Normal file
16
test/circuits/whilerolled.circom
Normal file
@@ -0,0 +1,16 @@
|
||||
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();
|
||||
12
test/circuits/whileunrolled.circom
Normal file
12
test/circuits/whileunrolled.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
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);
|
||||
51
utils/mergesymbols.js
Normal file
51
utils/mergesymbols.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const argv = require("yargs")
|
||||
.usage("mergesymbols -i [input_file] -o [output_file] -s [symbols file]")
|
||||
.alias("i", "input")
|
||||
.alias("o", "output")
|
||||
.alias("s", "symbols")
|
||||
.help("h")
|
||||
.epilogue(`Copyright (C) 2018 0kims association
|
||||
This program comes with ABSOLUTELY NO WARRANTY;
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; see the COPYING file in the official
|
||||
repo directory at https://github.com/iden3/circom `)
|
||||
.demandOption(["i","o","s"])
|
||||
.argv;
|
||||
|
||||
const inFileName = argv.input;
|
||||
const outFile = argv.output;
|
||||
const symbolsFile = argv.symbols;
|
||||
|
||||
let symbols;
|
||||
|
||||
async function loadSymbols() {
|
||||
symbols = {};
|
||||
const symsStr = await fs.promises.readFile(symbolsFile,"utf8");
|
||||
const lines = symsStr.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
const arr = lines[i].split(",");
|
||||
if (arr.length!=3) continue;
|
||||
symbols[arr[0]] = arr[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function run() {
|
||||
const outLines = [];
|
||||
await loadSymbols();
|
||||
const inStr = await fs.promises.readFile(inFileName,"utf8");
|
||||
const lines = inStr.split("\n");
|
||||
for (let i=0; i<lines.length; i++) {
|
||||
const arr = lines[i].split(" --> ");
|
||||
if (arr.length!=2) continue;
|
||||
outLines.push(symbols[arr[0]] + " --> " + arr[1]);
|
||||
}
|
||||
await fs.promises.writeFile(outFile,outLines.join("\n"), "utf8");
|
||||
}
|
||||
|
||||
|
||||
run().then(() => {
|
||||
process.exit(0);
|
||||
});
|
||||
Reference in New Issue
Block a user