Compare commits

...

19 Commits

Author SHA1 Message Date
Jordi Baylina
80846667ea 0.0.35 2019-12-04 21:54:11 +01:00
Jordi Baylina
7181c372d9 Error with bad assignments 2019-12-04 21:53:39 +01:00
Jordi Baylina
aecc28a79b Fix array assignement and not allow assign with equal 2019-12-04 17:52:52 +01:00
Jordi Baylina
51ff27b9c6 Verbose added 2019-09-22 12:56:05 +02:00
Jordi Baylina
6985892f86 0.0.34 2019-09-18 17:43:39 +02:00
Jordi Baylina
bacb7afde7 Merge branch 'master' of github.com:iden3/circom 2019-09-18 17:43:26 +02:00
Jordi Baylina
d04eff6c0d Optimize optimization and fix out<==in 2019-09-18 17:43:14 +02:00
Jordi Baylina
230894921e Merge pull request #41 from k06a/fix/error-message
Fix error message, for most IDEs recognises as URI to file row and column
2019-09-16 22:10:12 +02:00
Anton Bukov
64029e1842 Fix error message, for most IDEs recognises as URI to file row and column 2019-09-15 22:57:18 +03:00
Jordi Baylina
700412f23d 0.0.33 2019-09-15 18:55:40 +02:00
Jordi Baylina
832077fbe9 Fix Optimization 2019-09-15 18:55:28 +02:00
Jordi Baylina
0df0ac712d 0.0.32 2019-09-15 10:48:02 +02:00
Jordi Baylina
67a35ee400 better info in runtime constraint assertion 2019-09-15 10:47:52 +02:00
Jordi Baylina
680e3fe139 0.0.31 2019-08-29 16:26:28 +02:00
Jordi Baylina
f05c4e1338 compute block added 2019-08-29 16:26:19 +02:00
Jordi Baylina
597deb1eaa Merge pull request #31 from Mikerah/patch-1
Typo fixes
2019-06-27 11:40:21 +02:00
Jordi Baylina
7a1c606ca6 0.0.30 2019-06-21 10:43:11 +02:00
Jordi Baylina
6642d4cf93 Fix: include allways reduce constants 2019-06-21 10:42:49 +02:00
Mikerah
a43154241e Typo fixes 2019-06-10 20:41:01 -04:00
12 changed files with 541 additions and 305 deletions

View File

@@ -156,7 +156,7 @@ snarkjs verify
This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid. 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 `OK` or `INVALID` if not ok. If the proof is ok, you will see `OK` or `INVALID` if not ok.
@@ -182,7 +182,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. 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 ```sh
snarkjs generatecall snarkjs generatecall
@@ -192,7 +192,7 @@ 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 every thing works ok, this method should return true.
If you change any bit in the parameters, the result will be veryfiable false. If you change any bit in the parameters, the result will be verifiably false.
## Bonus track ## Bonus track
@@ -227,19 +227,19 @@ template Multiplier() {
component main = 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 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 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: the operation works in Zr, so we need to guarantee 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 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). Or a exponentiation in the Baby Jub curve [here](https://github.com/iden3/circomlib) (Work in progress).

14
cli.js
View File

@@ -34,8 +34,16 @@ const argv = require("yargs")
.alias("o", "output") .alias("o", "output")
.help("h") .help("h")
.alias("h", "help") .alias("h", "help")
.alias("v", "verbose") .option("verbose", {
.alias("f", "fast") alias: "v",
type: "boolean",
description: "Run with verbose logging"
})
.option("fast", {
alias: "f",
type: "boolean",
description: "Do not optimize constraints"
})
.epilogue(`Copyright (C) 2018 0kims association .epilogue(`Copyright (C) 2018 0kims association
This program comes with ABSOLUTELY NO WARRANTY; This program comes with ABSOLUTELY NO WARRANTY;
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
@@ -57,7 +65,7 @@ if (argv._.length == 0) {
const fullFileName = path.resolve(process.cwd(), inputFile); const fullFileName = path.resolve(process.cwd(), inputFile);
const outName = argv.output ? argv.output : "circuit.json"; const outName = argv.output ? argv.output : "circuit.json";
compiler(fullFileName, {reduceConstraints: !argv.fast}).then( (cir) => { compiler(fullFileName, {reduceConstraints: !argv.fast, verbose: argv.verbose}).then( (cir) => {
fs.writeFileSync(outName, JSON.stringify(cir, null, 1), "utf8"); fs.writeFileSync(outName, JSON.stringify(cir, null, 1), "utf8");
process.exit(0); process.exit(0);
}, (err) => { }, (err) => {

19
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "circom", "name": "circom",
"version": "0.0.29", "version": "0.0.35",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -432,10 +432,13 @@
} }
}, },
"eslint-utils": { "eslint-utils": {
"version": "1.3.1", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
"dev": true "dev": true,
"requires": {
"eslint-visitor-keys": "^1.0.0"
}
}, },
"eslint-visitor-keys": { "eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
@@ -868,9 +871,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.11", "version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true "dev": true
}, },
"map-age-cleaner": { "map-age-cleaner": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "circom", "name": "circom",
"version": "0.0.29", "version": "0.0.35",
"description": "Language to generate logic circuits", "description": "Language to generate logic circuits",
"main": "index.js", "main": "index.js",
"directories": { "directories": {

View File

@@ -40,6 +40,7 @@ if { return 'if'; }
else { return 'else'; } else { return 'else'; }
for { return 'for'; } for { return 'for'; }
while { return 'while'; } while { return 'while'; }
compute { return 'compute'; }
do { return 'do'; } do { return 'do'; }
return { return 'return'; } return { return 'return'; }
include { return 'include'; } include { return 'include'; }
@@ -198,6 +199,10 @@ statment
{ {
$$ = $1; $$ = $1;
} }
| computeStatment
{
$$ = $1;
}
| returnStatment | returnStatment
{ {
$$ = $1; $$ = $1;
@@ -302,6 +307,14 @@ doWhileStatment
} }
; ;
computeStatment
: 'compute' statment
{
$$ = { type: "COMPUTE", body: $2 };
setLines($$, @1, @2);
}
;
returnStatment returnStatment
: 'return' expression ';' : 'return' expression ';'
{ {

File diff suppressed because one or more lines are too long

View File

@@ -65,7 +65,8 @@ async function compile(srcFile, options) {
functions: {}, functions: {},
functionParams: {}, functionParams: {},
filePath: fullFilePath, filePath: fullFilePath,
fileName: fullFileName fileName: fullFileName,
verbose: options.verbose || false
}; };
@@ -75,10 +76,12 @@ async function compile(srcFile, options) {
throw new Error("A main component must be defined"); throw new Error("A main component must be defined");
} }
if (ctx.verbose) console.log("Classify Signals");
classifySignals(ctx); classifySignals(ctx);
if (options.reduceConstraints) { if (ctx.verbose) console.log("Reduce Constraints");
reduceConstants(ctx); reduceConstants(ctx);
if (options.reduceConstraints) {
// Repeat while reductions are performed // Repeat while reductions are performed
let oldNConstrains = -1; let oldNConstrains = -1;
@@ -217,6 +220,7 @@ function generateWitnessNames(ctx) {
function reduceConstants(ctx) { function reduceConstants(ctx) {
const newConstraints = []; const newConstraints = [];
for (let i=0; i<ctx.constraints.length; i++) { for (let i=0; i<ctx.constraints.length; i++) {
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
const c = lc.canonize(ctx, ctx.constraints[i]); const c = lc.canonize(ctx, ctx.constraints[i]);
if (!lc.isZero(c)) { if (!lc.isZero(c)) {
newConstraints.push(c); newConstraints.push(c);
@@ -226,8 +230,13 @@ function reduceConstants(ctx) {
} }
function reduceConstrains(ctx) { function reduceConstrains(ctx) {
const newConstraints = []; indexVariables();
for (let i=0; i<ctx.constraints.length; i++) { let possibleConstraints = Object.keys(ctx.constraints);
while (possibleConstraints.length>0) {
let nextPossibleConstraints = {};
for (let i in possibleConstraints) {
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constraints: ", i);
if (!ctx.constraints[i]) continue;
const c = ctx.constraints[i]; const c = ctx.constraints[i];
// Swap a and b if b has more variables. // Swap a and b if b has more variables.
@@ -254,6 +263,13 @@ function reduceConstrains(ctx) {
if (lc.isZero(c.a) || lc.isZero(c.b)) { if (lc.isZero(c.a) || lc.isZero(c.b)) {
const isolatedSignal = getFirstInternalSignal(ctx, c.c); const isolatedSignal = getFirstInternalSignal(ctx, c.c);
if (isolatedSignal) { if (isolatedSignal) {
let lSignal = ctx.signals[isolatedSignal];
while (lSignal.equivalence) {
lSignal = ctx.signals[lSignal.equivalence];
}
const isolatedSignalEquivalence = { const isolatedSignalEquivalence = {
type: "LINEARCOMBINATION", type: "LINEARCOMBINATION",
values: {} values: {}
@@ -268,24 +284,81 @@ function reduceConstrains(ctx) {
} }
} }
for (let j=0; j<newConstraints.length; j++) { for (let j in lSignal.inConstraints) {
newConstraints[j] = lc.substitute(newConstraints[j], isolatedSignal, isolatedSignalEquivalence); if ((j!=i)&&(ctx.constraints[j])) {
}
for (let j=i+1; j<ctx.constraints.length; j++ ) {
ctx.constraints[j] = lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence); ctx.constraints[j] = lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
linkSignalsConstraint(j);
if (j<i) {
nextPossibleConstraints[j] = true;
} }
c.a={ type: "LINEARCOMBINATION", values: {} };
c.b={ type: "LINEARCOMBINATION", values: {} };
c.c={ type: "LINEARCOMBINATION", values: {} };
isolatedSignal.category = "constant";
} }
} }
if (!lc.isZero(c)) { ctx.constraints[i] = null;
newConstraints.push(c);
lSignal.category = "constant";
} else {
if (lc.isZero(c.c)) ctx.constraints[i] = null;
} }
} }
ctx.constraints = newConstraints; }
possibleConstraints = Object.keys(nextPossibleConstraints);
}
unindexVariables();
// 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.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.values) linkSignal(k, cidx);
for (let k in ct.b.values) linkSignal(k, cidx);
for (let k in ct.c.values) linkSignal(k, cidx);
}
function unindexVariables() {
for (let s in ctx.signals) {
let lSignal = ctx.signals[s];
while (lSignal.equivalence) {
lSignal = ctx.signals[lSignal.equivalence];
}
if (lSignal.inConstraints) delete lSignal.inConstraints;
}
}
/*
function unlinkSignal(signalName, cidx) {
let lSignal = ctx.signals[signalName];
while (lSignal.equivalence) {
lSignal = ctx.signals[lSignal.equivalence];
}
if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
delete lSignal.inConstraints[cidx];
}
}
*/
function linkSignal(signalName, cidx) {
let lSignal = ctx.signals[signalName];
while (lSignal.equivalence) {
lSignal = ctx.signals[lSignal.equivalence];
}
if (!lSignal.inConstraints) lSignal.inConstraints = {};
lSignal.inConstraints[cidx] = true;
}
function getFirstInternalSignal(ctx, l) { function getFirstInternalSignal(ctx, l) {
for (let k in l.values) { for (let k in l.values) {

View File

@@ -131,6 +131,8 @@ function exec(ctx, ast) {
return execFunctionCall(ctx, ast); return execFunctionCall(ctx, ast);
} else if (ast.type == "BLOCK") { } else if (ast.type == "BLOCK") {
return execBlock(ctx, ast); return execBlock(ctx, ast);
} else if (ast.type == "COMPUTE") {
return ;
} else if (ast.type == "FOR") { } else if (ast.type == "FOR") {
return execFor(ctx, ast); return execFor(ctx, ast);
} else if (ast.type == "WHILE") { } else if (ast.type == "WHILE") {
@@ -179,24 +181,6 @@ function iterateSelectors(ctx, sizes, baseName, fn) {
return res; return res;
} }
function setScope(ctx, name, selectors, value) {
let l = getScopeLevel(ctx, name);
if (l==-1) l= ctx.scopes.length-1;
if (selectors.length == 0) {
ctx.scopes[l][name] = value;
} else {
setScopeArray(ctx.scopes[l][name], selectors);
}
function setScopeArray(a, sels) {
if (sels.length == 1) {
a[sels[0].value] = value;
} else {
setScopeArray(a[sels[0]], sels.slice(1));
}
}
}
function getScope(ctx, name, selectors) { function getScope(ctx, name, selectors) {
@@ -219,11 +203,57 @@ function getScope(ctx, name, selectors) {
} }
for (let i=ctx.scopes.length-1; i>=0; i--) { for (let i=ctx.scopes.length-1; i>=0; i--) {
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name], sels); if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels);
} }
return null; return null;
} }
function getScopeRef(ctx, name, selectors) {
const sels = [];
if (selectors) {
for (let i=0; i< selectors.length; i++) {
const idx = exec(ctx, selectors[i]);
if (ctx.error) return;
if (idx.type != "NUMBER") return error(ctx, selectors[i], "expected a number");
sels.push( idx.value.toJSNumber() );
}
}
function select(v, s, t) {
s = s || [];
if (s.length == 0) return [v, sels, t];
return select(v[s[0]], s.slice(1), t);
}
for (let i=ctx.scopes.length-1; i>=0; i--) {
if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels, ctx.scopes[i][name].type);
}
return [null, [], ""];
}
function setScopeRef(ctx, name, sels, value) {
let l = getScopeLevel(ctx, name);
if (l==-1) l= ctx.scopes.length-1;
if (sels.length == 0) {
ctx.scopes[l][name].value = value;
} else {
setScopeArray(ctx.scopes[l][name].value, sels);
}
function setScopeArray(a, sels) {
if (sels.length == 1) {
a[sels[0]] = value;
} else {
setScopeArray(a[sels[0]], sels.slice(1));
}
}
}
function getScopeLevel(ctx, name) { function getScopeLevel(ctx, name) {
for (let i=ctx.scopes.length-1; i>=0; i--) { for (let i=ctx.scopes.length-1; i>=0; i--) {
if (ctx.scopes[i][name]) return i; if (ctx.scopes[i][name]) return i;
@@ -246,12 +276,15 @@ function execTemplateDef(ctx, ast) {
return error(ctx, ast, "Name already exists: "+ast.name); return error(ctx, ast, "Name already exists: "+ast.name);
} }
scope[ast.name] = { scope[ast.name] = {
type: "TEMPLATE",
value: {
type: "TEMPLATE", type: "TEMPLATE",
params: ast.params, params: ast.params,
block: ast.block, block: ast.block,
fileName: ctx.fileName, fileName: ctx.fileName,
filePath: ctx.filePath, filePath: ctx.filePath,
scopes: copyScope(ctx.scopes) scopes: copyScope(ctx.scopes)
}
}; };
} }
@@ -263,12 +296,15 @@ function execFunctionDef(ctx, ast) {
} }
ctx.functionParams[ast.name] = ast.params; ctx.functionParams[ast.name] = ast.params;
scope[ast.name] = { scope[ast.name] = {
type: "FUNCTION",
value: {
type: "FUNCTION", type: "FUNCTION",
params: ast.params, params: ast.params,
block: ast.block, block: ast.block,
fileName: ctx.fileName, fileName: ctx.fileName,
filePath: ctx.filePath, filePath: ctx.filePath,
scopes: copyScope(ctx.scopes) scopes: copyScope(ctx.scopes)
}
}; };
} }
@@ -292,7 +328,9 @@ function execDeclareComponent(ctx, ast) {
} }
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) { scope[ast.name.name] = {
type: "COMPONENT",
value: iterateSelectors(ctx, sizes, baseName, function(fullName) {
ctx.components[fullName] = "UNINSTANTIATED"; ctx.components[fullName] = "UNINSTANTIATED";
@@ -300,7 +338,8 @@ function execDeclareComponent(ctx, ast) {
type: "COMPONENT", type: "COMPONENT",
fullName: fullName fullName: fullName
}; };
}); })
};
return { return {
type: "VARIABLE", type: "VARIABLE",
@@ -376,7 +415,10 @@ function execInstantiateComponet(ctx, vr, fn) {
const scope = {}; const scope = {};
for (let i=0; i< template.params.length; i++) { for (let i=0; i< template.params.length; i++) {
scope[template.params[i]] = paramValues[i]; scope[template.params[i]] = {
type: "VARIABLE",
value: paramValues[i]
};
ctx.components[ctx.currentComponent].params[template.params[i]] = extractValue(paramValues[i]); ctx.components[ctx.currentComponent].params[template.params[i]] = extractValue(paramValues[i]);
} }
@@ -428,7 +470,10 @@ function execFunctionCall(ctx, ast) {
const scope = {}; const scope = {};
for (let i=0; i< fnc.params.length; i++) { for (let i=0; i< fnc.params.length; i++) {
scope[fnc.params[i]] = paramValues[i]; scope[fnc.params[i]] = {
type: "VARIABLE",
value: paramValues[i]
};
} }
ctx.fileName = fnc.fileName; ctx.fileName = fnc.fileName;
@@ -470,7 +515,9 @@ function execDeclareSignal(ctx, ast) {
sizes.push( size.value.toJSNumber() ); sizes.push( size.value.toJSNumber() );
} }
scope[ast.name.name] = iterateSelectors(ctx, sizes, baseName, function(fullName) { scope[ast.name.name] = {
type: "SIGNAL",
value: iterateSelectors(ctx, sizes, baseName, function(fullName) {
ctx.signals[fullName] = { ctx.signals[fullName] = {
fullName: fullName, fullName: fullName,
direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""), direction: ast.declareType == "SIGNALIN" ? "IN" : (ast.declareType == "SIGNALOUT" ? "OUT" : ""),
@@ -484,7 +531,8 @@ function execDeclareSignal(ctx, ast) {
type: "SIGNAL", type: "SIGNAL",
fullName: fullName, fullName: fullName,
}; };
}); })
};
return { return {
type: "VARIABLE", type: "VARIABLE",
name: ast.name.name, name: ast.name.name,
@@ -507,12 +555,15 @@ function execDeclareVariable(ctx, ast) {
sizes.push( size.value.toJSNumber() ); sizes.push( size.value.toJSNumber() );
} }
scope[ast.name.name] = iterateSelectors(ctx, sizes, "", function() { scope[ast.name.name] = {
type: "VARIABLE",
value: iterateSelectors(ctx, sizes, "", function() {
return { return {
type: "NUMBER", type: "NUMBER",
value: bigInt(0) value: bigInt(0)
}; };
}); })
};
return { return {
type: "VARIABLE", type: "VARIABLE",
@@ -629,19 +680,20 @@ function execVarAssignement(ctx, ast) {
} else { } else {
v = ast.values[0]; v = ast.values[0];
} }
const num = getScope(ctx, v.name, v.selectors); const [num, sels, typ] = getScopeRef(ctx, v.name, v.selectors);
if (ctx.error) return; if (ctx.error) return;
if ((typeof(num) != "object")||(num == null)) return error(ctx, ast, "Variable not defined"); if ((typeof(num) != "object")||(num == null)) return error(ctx, ast, "Variable not defined");
if (num.type == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1]); if (typ == "COMPONENT") return execInstantiateComponet(ctx, v, ast.values[1]);
if (ctx.error) return; if (ctx.error) return;
// if (num.type == "SIGNAL") return error(ctx, ast, "Cannot assign to a signal with `=` use <-- or <== ops"); if ((typ == "SIGNAL")&&(ast.op == "=")) return error(ctx, ast, "Cannot assign to a signal with `=` use <-- or <== ops");
if ((["NUMBER", "COMPONENT"].indexOf(typ) >= 0 )&&(ast.op != "=")) return error(ctx, ast, `Cannot assign to a var with ${ast.op}. use = op`);
const res = exec(ctx, ast.values[1]); const res = exec(ctx, ast.values[1]);
if (ctx.error) return; if (ctx.error) return;
setScope(ctx, v.name, v.selectors, res); setScopeRef(ctx, v.name, sels, res);
return v; return v;
} }
@@ -913,13 +965,13 @@ function execMul(ctx, ast) {
function execVarAddAssignement(ctx, ast) { function execVarAddAssignement(ctx, ast) {
const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } ); const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
if (ctx.error) return; if (ctx.error) return;
return execVarAssignement(ctx, { values: [ast.values[0], res] }); return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
} }
function execVarMulAssignement(ctx, ast) { function execVarMulAssignement(ctx, ast) {
const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } ); const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
if (ctx.error) return; if (ctx.error) return;
return execVarAssignement(ctx, { values: [ast.values[0], res] }); return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
} }
function execPlusPlusRight(ctx, ast) { function execPlusPlusRight(ctx, ast) {
@@ -927,7 +979,7 @@ function execPlusPlusRight(ctx, ast) {
if (ctx.error) return; if (ctx.error) return;
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } ); const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
if (ctx.error) return; if (ctx.error) return;
execVarAssignement(ctx, { values: [ast.values[0], resAfter] }); execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
return resBefore; return resBefore;
} }
@@ -935,7 +987,7 @@ function execPlusPlusLeft(ctx, ast) {
if (ctx.error) return; if (ctx.error) return;
const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } ); const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
if (ctx.error) return; if (ctx.error) return;
execVarAssignement(ctx, { values: [ast.values[0], resAfter] }); execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
return resAfter; return resAfter;
} }
@@ -944,7 +996,7 @@ function execMinusMinusRight(ctx, ast) {
if (ctx.error) return; if (ctx.error) return;
const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } ); const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
if (ctx.error) return; if (ctx.error) return;
execVarAssignement(ctx, { values: [ast.values[0], resAfter] }); execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
return resBefore; return resBefore;
} }
@@ -952,7 +1004,7 @@ function execMinusMinusLeft(ctx, ast) {
if (ctx.error) return; if (ctx.error) return;
const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } ); const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
if (ctx.error) return; if (ctx.error) return;
execVarAssignement(ctx, { values: [ast.values[0], resAfter] }); execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
return resAfter; return resAfter;
} }
@@ -994,7 +1046,12 @@ function execSignalAssign(ctx, ast) {
let sDest=ctx.signals[dst.fullName]; let sDest=ctx.signals[dst.fullName];
if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName); if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.fullName);
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
let isOut = (sDest.component == "main")&&(sDest.direction=="OUT");
while (sDest.equivalence) {
sDest=ctx.signals[sDest.equivalence];
isOut = isOut || ((sDest.component == "main")&&(sDest.direction=="OUT"));
}
if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice"); if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
@@ -1022,11 +1079,21 @@ function execSignalAssign(ctx, ast) {
let assignValue = true; let assignValue = true;
if (src.type == "SIGNAL") { if (src.type == "SIGNAL") {
let sSrc = ctx.signals[src.fullName];
let isIn = (sSrc.component == "main")&&(sSrc.direction == "IN");
while (sSrc.equivalence) {
sSrc=ctx.signals[sSrc.equivalence];
isIn = isIn || ((sSrc.component == "main")&&(sSrc.direction == "IN"));
}
// Skip if an out is assigned directly to an input.
if ((!isIn)||(!isOut)) {
sDest.equivalence = src.fullName; sDest.equivalence = src.fullName;
sDest.alias = sDest.alias.concat(src.alias); sDest.alias = sDest.alias.concat(src.alias);
while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence]; while (sDest.equivalence) sDest=ctx.signals[sDest.equivalence];
assignValue = false; assignValue = false;
} }
}
if (assignValue) { if (assignValue) {
// const resLC = exec(ctx, vSrc); // const resLC = exec(ctx, vSrc);
@@ -1044,6 +1111,8 @@ function execSignalAssign(ctx, ast) {
} }
function execConstrain(ctx, ast) { function execConstrain(ctx, ast) {
ast.fileName = ctx.fileName;
ast.filePath = ctx.filePath;
const a = exec(ctx, ast.values[0]); const a = exec(ctx, ast.values[0]);
if (ctx.error) return; if (ctx.error) return;
const b = exec(ctx, ast.values[1]); const b = exec(ctx, ast.values[1]);
@@ -1056,6 +1125,8 @@ function execConstrain(ctx, ast) {
ctx.constraints.push(lc.toQEQ(res)); ctx.constraints.push(lc.toQEQ(res));
} }
if (ctx.constraints.length % 10000 == 0) console.log("Constraints: " + ctx.constraints.length);
return res; return res;
} }

View File

@@ -116,6 +116,8 @@ function gen(ctx, ast) {
return genFunctionCall(ctx, ast); return genFunctionCall(ctx, ast);
} else if (ast.type == "BLOCK") { } else if (ast.type == "BLOCK") {
return genBlock(ctx, ast); return genBlock(ctx, ast);
} else if (ast.type == "COMPUTE") {
return genCompute(ctx, ast);
} else if (ast.type == "FOR") { } else if (ast.type == "FOR") {
return genFor(ctx, ast); return genFor(ctx, ast);
} else if (ast.type == "WHILE") { } else if (ast.type == "WHILE") {
@@ -256,6 +258,12 @@ function genWhile(ctx, ast) {
return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`; return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`;
} }
function genCompute(ctx, ast) {
const body = gen(ctx, ast.body);
if (ctx.error) return;
return `{\n${body}\n}\n`;
}
function genIf(ctx, ast) { function genIf(ctx, ast) {
const condition = gen(ctx, ast.condition); const condition = gen(ctx, ast.condition);
if (ctx.error) return; if (ctx.error) return;
@@ -414,11 +422,13 @@ function genConstrain(ctx, ast) {
if (ctx.error) return; if (ctx.error) return;
const b = gen(ctx, ast.values[1]); const b = gen(ctx, ast.values[1]);
if (ctx.error) return; if (ctx.error) return;
return `ctx.assert(${a}, ${b})`; const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
return `ctx.assert(${a}, ${b}, \"${strErr}\")`;
} }
function genSignalAssignConstrain(ctx, ast) { function genSignalAssignConstrain(ctx, ast) {
return genVarAssignement(ctx, ast) + ";\n" + genConstrain(ctx, ast); // return genVarAssignement(ctx, ast) + ";\n" + genConstrain(ctx, ast);
return genVarAssignement(ctx, ast);
} }
function genVarAddAssignement(ctx, ast) { function genVarAddAssignement(ctx, ast) {

View File

@@ -49,4 +49,19 @@ describe("Sum test", () => {
// await compiler(path.join(__dirname, "circuits", "assignsignal.circom")); // await compiler(path.join(__dirname, "circuits", "assignsignal.circom"));
// }, /Cannot assign to a signal .*/); // }, /Cannot assign to a signal .*/);
// }); // });
it("Should compile a code with compute", async () => {
const cirDef = await compiler(path.join(__dirname, "circuits", "compute.circom"));
const circuit = new snarkjs.Circuit(cirDef);
const witness = circuit.calculateWitness({ "x": 6});
assert(witness[0].equals(bigInt(1)));
assert(witness[1].equals(bigInt(37)));
assert(witness[2].equals(bigInt(6)));
});
it("Should compile a code with compute", async () => {
const cirDef = await compiler(path.join(__dirname, "circuits", "inout.circom"));
assert.equal(cirDef.constraints.length, 1);
});
}); });

View 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();

View File

@@ -0,0 +1,18 @@
template Internal() {
signal input in;
signal output out;
out <== in;
}
template InOut() {
signal input in;
signal output out;
component internal = Internal();
internal.in <== in;
internal.out ==> out;
}
component main = InOut();