mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 03:06:42 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1965dd7f78 | ||
|
|
145a3eefeb | ||
|
|
38fa024745 | ||
|
|
5020d3f4ee | ||
|
|
ed63f08aeb | ||
|
|
c2a54e9187 | ||
|
|
d35d438107 | ||
|
|
5b45bafaac | ||
|
|
3f99f4eb53 |
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
||||
# Circom
|
||||
|
||||
Circon is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
||||
Circom is a language designed to write arithmetic circuits that can be used in zero knowledge proofs.
|
||||
|
||||
In particular, it is designed to work in [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||
|
||||
@@ -25,7 +25,7 @@ template NAND() {
|
||||
component main = NAND();
|
||||
```
|
||||
|
||||
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the constraints:
|
||||
The language uses mainly JavaScript/C syntax together with 5 extra operators to define the following constraints:
|
||||
|
||||
`<==` , `==>` : These two operators are used to connect signals and at the same time imply a constraint.
|
||||
|
||||
@@ -39,7 +39,7 @@ In the above example, both inputs are forced to be binary by adding the constrai
|
||||
|
||||
### Compilation the circuit
|
||||
|
||||
First of all, the compiler must be installed typing:
|
||||
First of all, the compiler must be installed by typing:
|
||||
|
||||
```
|
||||
npm install -g circom
|
||||
@@ -48,7 +48,7 @@ npm install -g circom
|
||||
The circuit is compiled with the following command:
|
||||
|
||||
```
|
||||
circom -s mycircuit.circom -o mycircuit.json
|
||||
circom mycircuit.circom -o mycircuit.json
|
||||
```
|
||||
|
||||
The resulting output ( `mycircuit.json` ) can be used in the [zksnarks JavaScript library](https://github.com/iden3/zksnark).
|
||||
@@ -91,7 +91,7 @@ in === out[0]*2**0 + out[1]*2**1 + out[2]*2**2 + ... + out[n-1]*2**(n-1)
|
||||
```
|
||||
|
||||
We do this by using a variable `lc1` and adding each signal multiplied by its coefficient.
|
||||
This variable does not hold a value in compilation time, but it holds a linear combination and it is used in the last constraint:
|
||||
This variable does not hold a value at compilation time, but it holds a linear combination and it is used in the last constraint:
|
||||
|
||||
```
|
||||
lc1 === in;
|
||||
@@ -256,7 +256,7 @@ In this example we have shown how to design a top-down circuit with many subcirc
|
||||
|
||||
## License
|
||||
|
||||
Circon is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details.
|
||||
Circom is part of the iden3 project copyright 2018 0KIMS association and published with GPL-3 license. Please check the COPYING file for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
1
cli.js
1
cli.js
@@ -61,6 +61,7 @@ compiler(fullFileName).then( (cir) => {
|
||||
process.exit(0);
|
||||
}, (err) => {
|
||||
// console.log(err);
|
||||
console.log(err.stack);
|
||||
if (err.pos) {
|
||||
console.error(`ERROR at ${err.errFile}:${err.pos.first_line},${err.pos.first_column}-${err.pos.last_line},${err.pos.last_column} ${err.errStr}`);
|
||||
} else {
|
||||
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.21",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1469,9 +1469,9 @@
|
||||
}
|
||||
},
|
||||
"snarkjs": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/snarkjs/-/snarkjs-0.1.3.tgz",
|
||||
"integrity": "sha512-z5HhuNt019ZzNzUztETK31rpjRRSz3Uzy8TjGgSROf+9ZT9i6dbdWkjTC3fh5o9H+R/2+hcR+7IKAmpIR56V+A==",
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/snarkjs/-/snarkjs-0.1.5.tgz",
|
||||
"integrity": "sha512-4GiP60ONIitWRnC5+Gsl7nIO62fvkGN9Y9jsDWBKORZI34eNXJBrMjhCbT+0X57FS2XjY0MsR0/Qvg2cs1H0sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big-integer": "^1.6.35",
|
||||
@@ -1481,9 +1481,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz",
|
||||
"integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==",
|
||||
"version": "6.5.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz",
|
||||
"integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
@@ -1514,9 +1514,9 @@
|
||||
}
|
||||
},
|
||||
"eslint": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
|
||||
"integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==",
|
||||
"version": "5.9.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.9.0.tgz",
|
||||
"integrity": "sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.21",
|
||||
"description": "Language to generate logic circuits",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
@@ -38,6 +38,6 @@
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"jison": "^0.4.18",
|
||||
"snarkjs": "0.1.5"
|
||||
"snarkjs": "0.1.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ include { return 'include'; }
|
||||
\- { return '-'; }
|
||||
\* { return '*'; }
|
||||
\/ { return '/'; }
|
||||
\\ { return '\\'; }
|
||||
\% { return '%'; }
|
||||
\^ { return '^'; }
|
||||
\& { return '&'; }
|
||||
@@ -118,7 +119,7 @@ include { return 'include'; }
|
||||
%left '<<' '>>'
|
||||
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%left '*' '/' '\\' '%'
|
||||
%left '**'
|
||||
%right '++' '--' UMINUS UPLUS '!' '~'
|
||||
%left '.'
|
||||
@@ -627,7 +628,7 @@ e7
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
let v = $3.value.greater(256) ? 256 : $3.value.value;
|
||||
$$ = {t1ype: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
|
||||
$$ = {type: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: ">>", values: [$1, $3] };
|
||||
}
|
||||
@@ -684,6 +685,15 @@ e5
|
||||
}
|
||||
setLines($$, @1, @3);
|
||||
}
|
||||
| e5 '\\' e4
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
$$ = { type: "NUMBER", value: ($1.value.divide($3.value)) };
|
||||
} else {
|
||||
$$ = { type: "OP", op: "\\", values: [$1, $3] };
|
||||
}
|
||||
setLines($$, @1, @3);
|
||||
}
|
||||
| e5 '%' e4
|
||||
{
|
||||
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
|
||||
|
||||
144
parser/jaz.js
144
parser/jaz.js
File diff suppressed because one or more lines are too long
71
src/exec.js
71
src/exec.js
@@ -80,10 +80,16 @@ function exec(ctx, ast) {
|
||||
return execPlusPlusLeft(ctx, ast);
|
||||
} else if (ast.op == "/") {
|
||||
return execDiv(ctx, ast);
|
||||
} else if (ast.op == "\\") {
|
||||
return execIDiv(ctx, ast);
|
||||
} else if (ast.op == "**") {
|
||||
return execExp(ctx, ast);
|
||||
} else if (ast.op == "&") {
|
||||
return execBAnd(ctx, ast);
|
||||
} else if (ast.op == "&&") {
|
||||
return execAnd(ctx, ast);
|
||||
} else if (ast.op == "||") {
|
||||
return execOr(ctx, ast);
|
||||
} else if (ast.op == "<<") {
|
||||
return execShl(ctx, ast);
|
||||
} else if (ast.op == ">>") {
|
||||
@@ -318,8 +324,7 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
const v = exec(ctx, fn.params[i]);
|
||||
if (ctx.error) return;
|
||||
|
||||
if (v.type != "NUMBER") return error(ctx, fn.params[i], "expected a number");
|
||||
paramValues.push( v.value);
|
||||
paramValues.push(v);
|
||||
}
|
||||
if (template.params.length != paramValues.length) error(ctx, fn, "Invalid Number of parameters");
|
||||
|
||||
@@ -330,6 +335,15 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
instantiateComponent(vv);
|
||||
|
||||
function instantiateComponent(varVal) {
|
||||
|
||||
function extractValue(v) {
|
||||
if (Array.isArray(v)) {
|
||||
return v.map(extractValue);
|
||||
} else {
|
||||
return v.value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(varVal)) {
|
||||
for (let i =0; i<varVal.length; i++) {
|
||||
instantiateComponent(varVal[i]);
|
||||
@@ -355,11 +369,8 @@ function execInstantiateComponet(ctx, vr, fn) {
|
||||
|
||||
const scope = {};
|
||||
for (let i=0; i< template.params.length; i++) {
|
||||
scope[template.params[i]] = {
|
||||
type: "NUMBER",
|
||||
value: paramValues[i]
|
||||
};
|
||||
ctx.components[ctx.currentComponent].params[template.params[i]] = paramValues[i];
|
||||
scope[template.params[i]] = paramValues[i];
|
||||
ctx.components[ctx.currentComponent].params[template.params[i]] = extractValue(paramValues[i]);
|
||||
}
|
||||
|
||||
ctx.components[ctx.currentComponent].template = templateName;
|
||||
@@ -541,6 +552,8 @@ function execPin(ctx, ast) {
|
||||
}
|
||||
|
||||
function execFor(ctx, ast) {
|
||||
|
||||
ctx.scopes.push({});
|
||||
exec(ctx, ast.init);
|
||||
if (ctx.error) return;
|
||||
|
||||
@@ -559,6 +572,7 @@ function execFor(ctx, ast) {
|
||||
if (ctx.error) return;
|
||||
}
|
||||
}
|
||||
ctx.scopes.pop();
|
||||
}
|
||||
|
||||
function execWhile(ctx, ast) {
|
||||
@@ -717,6 +731,34 @@ function execBAnd(ctx, ast) {
|
||||
};
|
||||
}
|
||||
|
||||
function execAnd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: (a.value.neq(0) && a.value.neq(0)) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execOr(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: (a.value.neq(0) || a.value.neq(0)) ? bigInt(1) : bigInt(0)
|
||||
};
|
||||
}
|
||||
|
||||
function execShl(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
@@ -791,6 +833,21 @@ function execDiv(ctx, ast) {
|
||||
};
|
||||
}
|
||||
|
||||
function execIDiv(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
if (a.type != "NUMBER") return { type: "NUMBER" };
|
||||
const b = exec(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
if (b.type != "NUMBER") return { type: "NUMBER" };
|
||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
||||
if (b.value.isZero()) return error(ctx, ast, "Division by zero");
|
||||
return {
|
||||
type: "NUMBER",
|
||||
value: a.value.divide(b.value)
|
||||
};
|
||||
}
|
||||
|
||||
function execAdd(ctx, ast) {
|
||||
const a = exec(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
@@ -67,8 +67,14 @@ function gen(ctx, ast) {
|
||||
return genExp(ctx, ast);
|
||||
} else if (ast.op == "/") {
|
||||
return genDiv(ctx, ast);
|
||||
} else if (ast.op == "\\") {
|
||||
return genIDiv(ctx, ast);
|
||||
} else if (ast.op == "&") {
|
||||
return genBAnd(ctx, ast);
|
||||
} else if (ast.op == "&&") {
|
||||
return genAnd(ctx, ast);
|
||||
} else if (ast.op == "||") {
|
||||
return genOr(ctx, ast);
|
||||
} else if (ast.op == "<<") {
|
||||
return genShl(ctx, ast);
|
||||
} else if (ast.op == ">>") {
|
||||
@@ -457,6 +463,15 @@ function genDiv(ctx, ast) {
|
||||
return `bigInt(${a}).mul( bigInt(${b}).inverse(__P__) ).mod(__P__)`;
|
||||
}
|
||||
|
||||
function genIDiv(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = gen(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
|
||||
return `bigInt(${a}).div( bigInt(${b}))`;
|
||||
}
|
||||
|
||||
function genExp(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
@@ -473,6 +488,22 @@ function genBAnd(ctx, ast) {
|
||||
return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`;
|
||||
}
|
||||
|
||||
function genAnd(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = gen(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
|
||||
}
|
||||
|
||||
function genOr(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const b = gen(ctx, ast.values[1]);
|
||||
if (ctx.error) return;
|
||||
return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`;
|
||||
}
|
||||
|
||||
function genShl(ctx, ast) {
|
||||
const a = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const chai = require("chai");
|
||||
const path = require("path");
|
||||
const snarkjs = require("snarkjs");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const bigInt = snarkjs.bigInt;
|
||||
|
||||
const compiler = require("../index.js");
|
||||
|
||||
@@ -11,4 +12,25 @@ 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)));
|
||||
});
|
||||
});
|
||||
|
||||
12
test/circuits/declareandistantiate.circom
Normal file
12
test/circuits/declareandistantiate.circom
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
template A() {
|
||||
signal a;
|
||||
}
|
||||
|
||||
template B() {
|
||||
component a[2] = A();
|
||||
}
|
||||
|
||||
component main = B();
|
||||
19
test/circuits/forvariables.circom
Normal file
19
test/circuits/forvariables.circom
Normal file
@@ -0,0 +1,19 @@
|
||||
template A() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
var acc = 0;
|
||||
for (var i=0; i<3; i++) {
|
||||
if (i==1) {
|
||||
var accIn = 0;
|
||||
for (var j=0; j<3; j++) {
|
||||
accIn= accIn+1;
|
||||
}
|
||||
acc = acc + accIn;
|
||||
}
|
||||
}
|
||||
|
||||
out <== in + acc;
|
||||
}
|
||||
|
||||
component main = A();
|
||||
14
test/circuits/mixvarsignal.circom
Normal file
14
test/circuits/mixvarsignal.circom
Normal file
@@ -0,0 +1,14 @@
|
||||
template X() {
|
||||
signal input i;
|
||||
signal output out;
|
||||
|
||||
var r = 0;
|
||||
for (var n=0; n<i; n++) {
|
||||
r++;
|
||||
}
|
||||
|
||||
i === r;
|
||||
out <== r;
|
||||
}
|
||||
|
||||
component main = X();
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user