mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-06 18:56:40 +01:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d5077106a | ||
|
|
b68c9fa5d2 | ||
|
|
94cb37bd9e | ||
|
|
9f15dd2e29 | ||
|
|
334697dd0c | ||
|
|
6c48e9ba01 | ||
|
|
42162b10c1 | ||
|
|
a84873161f | ||
|
|
9bf1354bf1 | ||
|
|
ed4c4b4de0 | ||
|
|
a1f3f714ea | ||
|
|
dafc9db614 | ||
|
|
6e4a192c6a | ||
|
|
e1286c29c7 | ||
|
|
a002bcbc25 | ||
|
|
f9f1d0c13e | ||
|
|
fc01aad8b6 | ||
|
|
3d5aa14cdc | ||
|
|
5dc54bb7d2 | ||
|
|
349a39dc95 | ||
|
|
0a2aee9932 | ||
|
|
bad8fad3d0 | ||
|
|
0a707648fc | ||
|
|
263dc73906 | ||
|
|
8e730d93a4 | ||
|
|
bbe10438e0 | ||
|
|
c2cef8d80c | ||
|
|
2200408986 | ||
|
|
5f13d37fdc | ||
|
|
3b46b74d4a | ||
|
|
aa2d768465 | ||
|
|
f02ceb2508 | ||
|
|
d014d67032 | ||
|
|
2e1b35a94d | ||
|
|
7cef1be2c3 | ||
|
|
4b631994ca |
@@ -1,12 +1,15 @@
|
||||
# Circom
|
||||
|
||||
Circom 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).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Circom Docs
|
||||
|
||||
You can read the details of circom in [the documentation webpage](https://docs.circom.io/).
|
||||
|
||||
### Tutorial
|
||||
|
||||
A good starting point [is this tutorial](https://github.com/iden3/circom/blob/master/TUTORIAL.md)
|
||||
|
||||
6
cli.js
6
cli.js
@@ -24,6 +24,7 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const stringifyBigInts = require("ffjavascript").utils.stringifyBigInts;
|
||||
const fastFile = require("fastfile");
|
||||
|
||||
const compiler = require("./src/compiler");
|
||||
@@ -87,6 +88,8 @@ async function run() {
|
||||
|
||||
if (argv.csource) {
|
||||
options.cSourceFile = await fastFile.createOverride(cSourceName);
|
||||
const noExt = cSourceName.substr(0, cSourceName.lastIndexOf(".")) || cSourceName;
|
||||
options.dataFile = await fastFile.createOverride(noExt+".dat");
|
||||
}
|
||||
if (argv.wasm) {
|
||||
options.wasmFile = await fastFile.createOverride(wasmName);
|
||||
@@ -117,6 +120,7 @@ async function run() {
|
||||
await compiler(fullFileName, options);
|
||||
|
||||
if (options.cSourceFile) await options.cSourceFile.close();
|
||||
if (options.dataFile) await options.dataFile.close();
|
||||
if (options.wasmFile) await options.wasmFile.close();
|
||||
if (options.watFile) await options.watFile.close();
|
||||
let symDone = false;
|
||||
@@ -150,7 +154,7 @@ run().then(()=> {
|
||||
if (argv.verbose) console.log(err.stack);
|
||||
}
|
||||
if (err.ast) {
|
||||
console.error(JSON.stringify(err.ast, null, 1));
|
||||
console.error(JSON.stringify(stringifyBigInts(err.ast), null, 1));
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
86
package-lock.json
generated
86
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.5.18",
|
||||
"version": "0.5.34",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -30,6 +30,11 @@
|
||||
"js-tokens": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@iden3/bigarray": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@iden3/bigarray/-/bigarray-0.0.2.tgz",
|
||||
"integrity": "sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g=="
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
@@ -203,20 +208,22 @@
|
||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
|
||||
},
|
||||
"circom_runtime": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.0.6.tgz",
|
||||
"integrity": "sha512-o0T5MuWzxnxinWG3+CygS/kZouoP+z5ZrufUwqKJy3gsVFJhkbqMpfKmcBGjhExB3uatA7cKyOiRAOLOz5+t5w==",
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.9.tgz",
|
||||
"integrity": "sha512-eh34quaGpeEWXthnhmC9zpoBL/5zJ0mGDbPT/plb/xVmFaKxJDcLuCr2sma5s3il8AYlEIb/nbqytojyI3TWDQ==",
|
||||
"requires": {
|
||||
"ffjavascript": "0.1.0",
|
||||
"ffjavascript": "0.2.10",
|
||||
"fnv-plus": "^1.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ffjavascript": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.1.0.tgz",
|
||||
"integrity": "sha512-dmKlUasSfvUcxBm8nCSKl2x7EFJsXA7OVP8XLFA03T2+6mAc3IiVLC2ambEVOcMOhyhl0vJfVZjM9f9d38D1rw==",
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.10.tgz",
|
||||
"integrity": "sha512-GQI6gHYYG5/iD4Kt3VzezzK7fARJzP0zkc82V/+JAdjfeKBXhDSo5rpKFuK3cDcrdW0Fu2emuYNMEAuFqhEQvQ==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.48"
|
||||
"big-integer": "^1.6.48",
|
||||
"wasmcurves": "0.0.5",
|
||||
"worker-threads": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,9 +351,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ejs": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz",
|
||||
"integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz",
|
||||
"integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==",
|
||||
"requires": {
|
||||
"jake": "^10.6.1"
|
||||
}
|
||||
@@ -576,14 +583,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"fastfile": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.12.tgz",
|
||||
"integrity": "sha512-0EZo2y5eW8X0oiDDRvcnufjVxlM96CQL5hvmRQtbRABWlCkH73IHwkzl0qOSdxtchaMr+0TSB7GVqaVEixRr1Q=="
|
||||
"version": "0.0.18",
|
||||
"resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.18.tgz",
|
||||
"integrity": "sha512-q03PTKc+wptis4WmuFOwPNQx2p5myFUrl/dMgRlW9mymc1Egyc14JPHgiGnWK+sJ0+dBl2Vwtfh5GfSQltYOpw=="
|
||||
},
|
||||
"ffiasm": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ffiasm/-/ffiasm-0.0.2.tgz",
|
||||
"integrity": "sha512-o/CL7F4IodB7eRHCOQL1SrqN2DIPHrQbEwjPY7NIyeBRdnB3G0xo6b6Mj44SKiWFnvpQMb3n4N7acjD3vv4NVQ==",
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ffiasm/-/ffiasm-0.1.1.tgz",
|
||||
"integrity": "sha512-irMMHiR9JJ7BVBrAhtliUawxVdPYSdyl81taUYJ4C1mJ0iw2ueThE/qtr0J8B83tsIY8HJvh0lg5F+6ClK4xpA==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.48",
|
||||
"ejs": "^3.0.1",
|
||||
@@ -591,13 +598,24 @@
|
||||
}
|
||||
},
|
||||
"ffjavascript": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.4.tgz",
|
||||
"integrity": "sha512-XFeWcjUDFPavN+DDOxhE8p5MOhZQJc9oO1Sj4ml1pyjqNhS1ujEamcjFyK0cctdnat61i7lvpTYzdtS3RYDC8w==",
|
||||
"version": "0.2.22",
|
||||
"resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.22.tgz",
|
||||
"integrity": "sha512-EsVqap2Txm17bKW0z/jXCX3M7rQ++nQUAJY8alWDpyhjRj90xjl6GLeVSKZQ8rOFDQ/SFFXcEB8w9X8Boxid+w==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.48",
|
||||
"wasmcurves": "0.0.4",
|
||||
"wasmcurves": "0.0.12",
|
||||
"worker-threads": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"wasmcurves": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.0.12.tgz",
|
||||
"integrity": "sha512-1Jl9mkatyHSNj80ILjf85SZUNuZQBCkTjJlhzqHnZQXUmIimCIWkugaVaYNjozLs1Gun4h/keZe1MBeBN0sRpg==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.42",
|
||||
"blakejs": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ffwasm": {
|
||||
@@ -1161,19 +1179,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"r1csfile": {
|
||||
"version": "0.0.14",
|
||||
"resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.14.tgz",
|
||||
"integrity": "sha512-7m4eWpnbjkwGGUaRmIAJc4w+HvaeBPJXUKHIqLkHeD9Yyjem6/EHmlgDVl+4hWNWGZqdhXuMqWSH9+O6QOXBdw==",
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.16.tgz",
|
||||
"integrity": "sha512-A2jRVWzGgmXeG2lVAc0H4suJmzt50it5UvBnycJgBCpMXM3tH/M6RguP7nvs6suY/yYnkN6jX6iTScSiDUF3FA==",
|
||||
"requires": {
|
||||
"fastfile": "0.0.7",
|
||||
"ffjavascript": "0.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"fastfile": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.7.tgz",
|
||||
"integrity": "sha512-Zk7sdqsV6DsN/rhjULDfCCowPiMDsziTMFicdkrKN80yybr/6YFf9H91ELXN85dVEf6EYkVR5EHkZNc0dMqZKA=="
|
||||
}
|
||||
"@iden3/bigarray": "0.0.2",
|
||||
"fastfile": "0.0.18",
|
||||
"ffjavascript": "0.2.22"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
@@ -1472,9 +1484,9 @@
|
||||
}
|
||||
},
|
||||
"wasmcurves": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.0.4.tgz",
|
||||
"integrity": "sha512-c/Tob+F/7jJhep1b2qtj54r4nkGaRifNbQ1OJx8cBBFH1RlHbWIbISHWONClOxiVwy/JZOpbN4SgvSX/4lF80A==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.0.5.tgz",
|
||||
"integrity": "sha512-BmI4GXLjLawGg2YkvHa8zRsnWec+d1uwoxE+Iov8cqOpDL7GA5XO2pk2yuDbXHMzwIug2exnKot3baRZ86R0pA==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.42",
|
||||
"blakejs": "^1.1.0"
|
||||
|
||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "circom",
|
||||
"version": "0.5.18",
|
||||
"version": "0.5.34",
|
||||
"description": "Language to generate logic circuits",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
@@ -30,13 +30,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"circom_runtime": "0.0.6",
|
||||
"fastfile": "0.0.12",
|
||||
"ffiasm": "0.0.2",
|
||||
"ffjavascript": "0.2.4",
|
||||
"circom_runtime": "0.1.9",
|
||||
"fastfile": "0.0.18",
|
||||
"ffiasm": "0.1.1",
|
||||
"ffjavascript": "0.2.22",
|
||||
"ffwasm": "0.0.7",
|
||||
"fnv-plus": "^1.3.1",
|
||||
"r1csfile": "0.0.14",
|
||||
"r1csfile": "0.0.16",
|
||||
"tmp-promise": "^2.0.2",
|
||||
"wasmbuilder": "0.0.10"
|
||||
},
|
||||
|
||||
@@ -59,8 +59,9 @@ class CodeBuilderC {
|
||||
this.ops.push({op: "SETSIGNAL", component, signal, value});
|
||||
}
|
||||
|
||||
getSignal(dLabel, component, signal) {
|
||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
|
||||
getSignal(dLabel, component, signal, n) {
|
||||
if (typeof(n) == "undefined") n=1;
|
||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n});
|
||||
}
|
||||
|
||||
copyN(dLabel, offset, src, n) {
|
||||
@@ -95,6 +96,10 @@ class CodeBuilderC {
|
||||
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
|
||||
}
|
||||
|
||||
checkAssert(a, strErr) {
|
||||
this.ops.push({op: "CHECKASSERT", a, strErr});
|
||||
}
|
||||
|
||||
log(val) {
|
||||
this.ops.push({op: "LOG", val});
|
||||
}
|
||||
@@ -177,7 +182,7 @@ class CodeBuilderC {
|
||||
} 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});`);
|
||||
code.push(`ctx->multiGetSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel}, ${o.n});`);
|
||||
} else if (o.op == "COPYN") {
|
||||
const oS = ref2src(o.offset);
|
||||
const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel;
|
||||
@@ -215,6 +220,8 @@ class CodeBuilderC {
|
||||
code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`);
|
||||
} else if (o.op == "CHECKCONSTRAINT") {
|
||||
code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`);
|
||||
} else if (o.op == "CHECKASSERT") {
|
||||
code.push(`ctx->checkAssert(__cIdx, ${ref2src(o.a)}, "${o.strErr}");`);
|
||||
} else if (o.op == "LOG") {
|
||||
code.push(`ctx->log(${ref2src(o.val)});`);
|
||||
}
|
||||
@@ -352,6 +359,12 @@ class BuilderC {
|
||||
this.components = new BigArray();
|
||||
this.usedConstants = {};
|
||||
this.verbose = verbose;
|
||||
|
||||
|
||||
this.sizePointers = {};
|
||||
this.hashMapPointers = {};
|
||||
this.functionIdx = {};
|
||||
this.nCets = 0;
|
||||
}
|
||||
|
||||
setHeader(header) {
|
||||
@@ -363,11 +376,11 @@ class BuilderC {
|
||||
this.hashMaps[name] = hm;
|
||||
}
|
||||
|
||||
addComponentEntriesTable(name, cet) {
|
||||
this.componentEntriesTables.push({
|
||||
addComponentEntriesTable(name, cet, idComponent) {
|
||||
this.componentEntriesTables[idComponent] = {
|
||||
name: name,
|
||||
cet: cet
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
addSizes(name, accSizes) {
|
||||
@@ -413,82 +426,104 @@ class BuilderC {
|
||||
|
||||
_buildHeader(code) {
|
||||
code.push(
|
||||
"#include \"circom.h\"",
|
||||
"#include \"calcwit.h\"",
|
||||
"#include \"circom.hpp\"",
|
||||
"#include \"calcwit.hpp\"",
|
||||
`#define NSignals ${this.header.NSignals}`,
|
||||
`#define NComponents ${this.header.NComponents}`,
|
||||
`#define NOutputs ${this.header.NOutputs}`,
|
||||
`#define NInputs ${this.header.NInputs}`,
|
||||
`#define NVars ${this.header.NVars}`,
|
||||
`#define NPublic ${this.header.NPublic}`,
|
||||
`#define __P__ "${this.header.P.toString()}"`,
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
_buildHashMaps(code) {
|
||||
async _buildHashMaps(fdData) {
|
||||
|
||||
code.push("// Hash Maps ");
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pHashMaps = fdData.pos;
|
||||
|
||||
const buff = new Uint8Array(256*12);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
for (let hmName in this.hashMaps ) {
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.hashMapPointers[hmName] = fdData.pos;
|
||||
const hm = this.hashMaps[hmName];
|
||||
|
||||
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}";
|
||||
}
|
||||
buffV.setUint32(i*12, hm[i] ? parseInt( hm[i][0].slice(8), 16 ) : 0, true);
|
||||
buffV.setUint32(i*12+4, hm[i] ? parseInt( hm[i][0].slice(0,8), 16 ) : 0, true);
|
||||
buffV.setUint32(i*12+8, hm[i] ? hm[i][1] : 0, true);
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
|
||||
await fdData.write(buff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_buildComponentEntriesTables(code) {
|
||||
code.push("// Component Entry tables");
|
||||
async _buildComponentEntriesTables(fdData) {
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pCets = fdData.pos;
|
||||
for (let i=0; i< this.componentEntriesTables.length; i++) {
|
||||
if ((this.verbose)&&(i%100000 ==0)) console.log(`_buildComponentEntriesTables ${i}/${this.componentEntriesTables.length}`);
|
||||
const cetName = this.componentEntriesTables[i].name;
|
||||
const cet = this.componentEntriesTables[i].cet;
|
||||
|
||||
code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`);
|
||||
this.components[i].entryTablePointer = fdData.pos;
|
||||
const buff = new Uint8Array(16*cet.length);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
for (let j=0; j<cet.length; j++) {
|
||||
const ty = cet[j].type == "S" ? "_typeSignal" : "_typeComponent";
|
||||
code.push(` ${j>0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`);
|
||||
utils.setUint64(buffV, 16*j+0, this.sizePointers[ cet[j].sizeName]);
|
||||
buffV.setUint32(16*j+8, cet[j].offset, true);
|
||||
buffV.setUint32(16*j+12, cet[j].type == "S" ? 0 : 1, true); // Size type 0-> Signal, 1->Component
|
||||
this.nCets ++;
|
||||
}
|
||||
code.push("};");
|
||||
|
||||
await fdData.write(buff);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_buildSizes(code) {
|
||||
code.push("// Sizes");
|
||||
async _buildSizes(fdData) {
|
||||
for (let sName in this.sizes) {
|
||||
const accSizes = this.sizes[sName];
|
||||
|
||||
let c = `Circom_Size ${sName}[${accSizes.length}] = {`;
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.sizePointers[sName] = fdData.pos;
|
||||
|
||||
const buff = new Uint8Array(4*accSizes.length);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
for (let i=0; i<accSizes.length; i++) {
|
||||
if (i>0) c += ",";
|
||||
c += accSizes[i];
|
||||
buffV.setUint32(i*4, accSizes[i], true);
|
||||
}
|
||||
c += "};";
|
||||
code.push(c);
|
||||
await fdData.write(buff);
|
||||
}
|
||||
}
|
||||
|
||||
_buildConstants(code) {
|
||||
|
||||
async _buildConstants(fdData) {
|
||||
const self = this;
|
||||
|
||||
code.push("// Constants");
|
||||
code.push(`FrElement _constants[${self.constants.length}] = {`);
|
||||
for (let i=0; i<self.constants.length; i++) {
|
||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildConstants ${i}/${this.constants.length}`);
|
||||
code.push((i>0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}");
|
||||
}
|
||||
code.push("};");
|
||||
const frSize = (8 + self.F.n64*8);
|
||||
const buff = new Uint8Array(self.constants.length* frSize);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
function number2Code(n) {
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pConstants = fdData.pos;
|
||||
|
||||
let o = 0;
|
||||
for (let i=0; i<self.constants.length; i++) {
|
||||
Fr2Bytes(buffV, o, self.constants[i]);
|
||||
o += frSize;
|
||||
}
|
||||
await fdData.write(buff);
|
||||
|
||||
|
||||
function Fr2Bytes(buffV, offset, n) {
|
||||
const minShort = self.F.neg(self.F.e("80000000"));
|
||||
const maxShort = self.F.e("7FFFFFFF", 16);
|
||||
|
||||
@@ -496,51 +531,49 @@ class BuilderC {
|
||||
&&(self.F.leq(n, maxShort)))
|
||||
{
|
||||
if (self.F.geq(n, self.F.zero)) {
|
||||
return addShortMontgomeryPositive(n);
|
||||
return shortMontgomeryPositive(n);
|
||||
} else {
|
||||
return addShortMontgomeryNegative(n);
|
||||
return shortMontgomeryNegative(n);
|
||||
}
|
||||
}
|
||||
|
||||
return addLongMontgomery(n);
|
||||
return longMontgomery(n);
|
||||
|
||||
|
||||
function addShortMontgomeryPositive(a) {
|
||||
return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
function shortMontgomeryPositive(a) {
|
||||
buffV.setUint32(offset, Scalar.toNumber(a) , true );
|
||||
buffV.setUint32(offset + 4, 0x40000000 , true );
|
||||
long(buffV, offset + 8, toMontgomery(a));
|
||||
}
|
||||
|
||||
|
||||
function addShortMontgomeryNegative(a) {
|
||||
function shortMontgomeryNegative(a) {
|
||||
const b = -Scalar.toNumber(self.F.neg(a));
|
||||
return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
buffV.setUint32(offset, b , true );
|
||||
buffV.setUint32(offset + 4, 0x40000000 , true );
|
||||
long(buffV, offset + 8, toMontgomery(a));
|
||||
}
|
||||
|
||||
function addLongMontgomery(a) {
|
||||
return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`;
|
||||
function longMontgomery(a) {
|
||||
buffV.setUint32(offset, 0 , true );
|
||||
buffV.setUint32(offset + 4, 0xC0000000 , true );
|
||||
long(buffV, offset + 8, toMontgomery(a));
|
||||
}
|
||||
|
||||
function getLongString(a) {
|
||||
let S = "";
|
||||
function long(buffV, offset, a) {
|
||||
|
||||
let p = offset;
|
||||
const arr = Scalar.toArray(a, 0x100000000);
|
||||
for (let i=0; i<self.F.n64*2; i+=2) {
|
||||
const idx = arr.length-2-i;
|
||||
|
||||
if (i>0) S = S + ",";
|
||||
for (let i=0; i<self.F.n64*2; i++) {
|
||||
const idx = arr.length-1-i;
|
||||
|
||||
if ( idx >=0) {
|
||||
let msb = arr[idx].toString(16);
|
||||
while (msb.length<8) msb = "0" + msb;
|
||||
|
||||
let lsb = arr[idx+1].toString(16);
|
||||
while (lsb.length<8) lsb = "0" + lsb;
|
||||
|
||||
S += "0x" + msb + lsb + "LL";
|
||||
buffV.setUint32(p, arr[idx], true);
|
||||
} else {
|
||||
S += "0LL";
|
||||
buffV.setUint32(p, 0, true);
|
||||
}
|
||||
p+= 4;
|
||||
}
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
function toMontgomery(a) {
|
||||
@@ -548,101 +581,139 @@ class BuilderC {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_buildFunctions(code) {
|
||||
const listedFunctions = [];
|
||||
for (let i=0; i<this.functions.length; i++) {
|
||||
const cfb = this.functions[i];
|
||||
cfb.build(code);
|
||||
if (this.functions[i].type == "COMPONENT") {
|
||||
this.functionIdx[this.functions[i].name] = listedFunctions.length;
|
||||
listedFunctions.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
code.push("// Function Table");
|
||||
code.push(`Circom_ComponentFunction _functionTable[${listedFunctions.length}] = {`);
|
||||
for (let i=0; i<listedFunctions.length; i++) {
|
||||
const sep = i>0 ? " ," : " ";
|
||||
code.push(`${sep}${this.functions[listedFunctions[i]].name}`);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildComponents(code) {
|
||||
code.push("// Components");
|
||||
code.push(`Circom_Component _components[${this.components.length}] = {`);
|
||||
|
||||
async _buildComponents(fdData) {
|
||||
|
||||
const buff = new Uint8Array(32);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pComponents = fdData.pos;
|
||||
|
||||
for (let i=0; i<this.components.length; i++) {
|
||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildComponents ${i}/${this.components.length}`);
|
||||
const c = this.components[i];
|
||||
const sep = i>0 ? " ," : " ";
|
||||
code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`);
|
||||
|
||||
utils.setUint64(buffV, 0, this.hashMapPointers[c.hashMapName], true);
|
||||
utils.setUint64(buffV, 8, c.entryTablePointer, true);
|
||||
utils.setUint64(buffV, 16, this.functionIdx[c.functionName], true);
|
||||
buffV.setUint32(24, c.nInSignals, true);
|
||||
buffV.setUint32(28, c.newThread ? 1 : 0, true);
|
||||
|
||||
await fdData.write(buff);
|
||||
}
|
||||
code.push("};");
|
||||
}
|
||||
|
||||
_buildMapIsInput(code) {
|
||||
code.push("// mapIsInput");
|
||||
code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`);
|
||||
let line = "";
|
||||
async _buildMapIsInput(fdData) {
|
||||
|
||||
const buff = new Uint8Array(this.mapIsInput.length * 4);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pMapIsInput = fdData.pos;
|
||||
|
||||
for (let i=0; i<this.mapIsInput.length; i++) {
|
||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildMapIsInput ${i}/${this.mapIsInput.length}`);
|
||||
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;
|
||||
buffV.setUint32(4*i, this.mapIsInput[i], true);
|
||||
}
|
||||
|
||||
await fdData.write(buff);
|
||||
}
|
||||
|
||||
_buildWit2Sig(code) {
|
||||
code.push("// Witness to Signal Table");
|
||||
code.push(`int _wit2sig[${this.wit2sig.length}] = {`);
|
||||
let line = "";
|
||||
async _buildWit2Sig(fdData) {
|
||||
|
||||
const buff = new Uint8Array(this.wit2sig.length * 4);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
this.pWit2Sig = fdData.pos;
|
||||
|
||||
for (let i=0; i<this.wit2sig.length; i++) {
|
||||
if ((this.verbose)&&(i%1000000 ==0)) console.log(`_buildWit2Sig ${i}/${this.wit2sig.length}`);
|
||||
line += i>0 ? "," : " ";
|
||||
line += this.wit2sig[i];
|
||||
if (((i+1) % 64) == 0) {
|
||||
code.push(" "+line);
|
||||
line = "";
|
||||
}
|
||||
|
||||
buffV.setUint32(4*i, this.wit2sig[i], true);
|
||||
}
|
||||
if (line != "") code.push(" "+line);
|
||||
code.push("};");
|
||||
|
||||
await fdData.write(buff);
|
||||
}
|
||||
|
||||
_buildCircuitVar(code) {
|
||||
async _buildCircuitVar(fdData) {
|
||||
|
||||
code.push(
|
||||
"// Circuit Variable",
|
||||
"Circom_Circuit _circuit = {" ,
|
||||
" NSignals,",
|
||||
" NComponents,",
|
||||
" NInputs,",
|
||||
" NOutputs,",
|
||||
" NVars,",
|
||||
" _wit2sig,",
|
||||
" _components,",
|
||||
" _mapIsInput,",
|
||||
" _constants,",
|
||||
" __P__",
|
||||
"};"
|
||||
);
|
||||
const buff = new Uint8Array(76);
|
||||
const buffV = new DataView(buff.buffer);
|
||||
|
||||
utils.setUint64(buffV, 0, this.pWit2Sig, true);
|
||||
utils.setUint64(buffV, 8, this.pComponents, true);
|
||||
utils.setUint64(buffV, 16, this.pMapIsInput, true);
|
||||
utils.setUint64(buffV, 24, this.pConstants, true);
|
||||
utils.setUint64(buffV, 32, this.pPriemStr, true);
|
||||
utils.setUint64(buffV, 40, this.pCets, true);
|
||||
|
||||
buffV.setUint32(48, this.header.NSignals, true);
|
||||
buffV.setUint32(52, this.header.NComponents, true);
|
||||
buffV.setUint32(56, this.header.NOutputs, true);
|
||||
buffV.setUint32(60, this.header.NInputs, true);
|
||||
buffV.setUint32(64, this.header.NVars, true);
|
||||
buffV.setUint32(68, this.nCets, true);
|
||||
buffV.setUint32(72, this.header.NPublic, true);
|
||||
|
||||
fdData.pos = 0;
|
||||
|
||||
await fdData.write(buff);
|
||||
}
|
||||
|
||||
async _buildPrimeStr(fdData) {
|
||||
this.pPriemStr = fdData.pos;
|
||||
const strBuff = new TextEncoder("utf-8").encode(this.header.P.toString());
|
||||
await fdData.write(strBuff);
|
||||
|
||||
const zB = new Uint8Array(1);
|
||||
zB[0] =0;
|
||||
await fdData.write(zB);
|
||||
}
|
||||
|
||||
|
||||
async build(fd) {
|
||||
async build(fdCode, fdData) {
|
||||
const encoder = new TextEncoder("utf-8");
|
||||
fdData.pos = 76;
|
||||
while (fdData.pos % 8) fdData.pos++;
|
||||
|
||||
const code=new BigArray();
|
||||
this._buildHeader(code);
|
||||
this._buildSizes(code);
|
||||
this._buildConstants(code);
|
||||
this._buildHashMaps(code);
|
||||
this._buildComponentEntriesTables(code);
|
||||
await this._buildPrimeStr(fdData);
|
||||
await this._buildSizes(fdData);
|
||||
await this._buildConstants(fdData);
|
||||
await this._buildHashMaps(fdData);
|
||||
await this._buildComponentEntriesTables(fdData);
|
||||
this._buildFunctions(code);
|
||||
this._buildComponents(code);
|
||||
this._buildMapIsInput(code);
|
||||
this._buildWit2Sig(code);
|
||||
this._buildCircuitVar(code);
|
||||
await this._buildComponents(fdData);
|
||||
await this._buildMapIsInput(fdData);
|
||||
await this._buildWit2Sig(fdData);
|
||||
await this._buildCircuitVar(fdData);
|
||||
await writeCode(code);
|
||||
|
||||
async function writeCode(c) {
|
||||
@@ -651,7 +722,7 @@ class BuilderC {
|
||||
await writeCode(c[i]);
|
||||
}
|
||||
} else if (typeof c === "string") {
|
||||
await fd.write(encoder.encode(c + "\n"));
|
||||
await fdCode.write(encoder.encode(c + "\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ async function c_tester(circomFile, _options) {
|
||||
const options = Object.assign({}, _options);
|
||||
|
||||
options.cSourceFile = await fastFile.createOverride(path.join(dir.path, baseName + ".cpp"));
|
||||
options.dataFile = await fastFile.createOverride(path.join(dir.path, baseName + ".dat"));
|
||||
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
|
||||
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
|
||||
|
||||
@@ -38,14 +39,15 @@ async function c_tester(circomFile, _options) {
|
||||
await compiler(circomFile, options);
|
||||
|
||||
await options.cSourceFile.close();
|
||||
await options.dataFile.close();
|
||||
|
||||
const source = await buildZqField(options.p, "Fr");
|
||||
|
||||
// console.log(dir.path);
|
||||
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.asm"), source.asm, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.h"), source.h, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.c"), source.c, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.hpp"), source.hpp, "utf8");
|
||||
await fs.promises.writeFile(path.join(dir.path, "fr.cpp"), source.cpp, "utf8");
|
||||
|
||||
let pThread = "";
|
||||
|
||||
@@ -66,7 +68,7 @@ async function c_tester(circomFile, _options) {
|
||||
` ${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.cpp")}` +
|
||||
` ${path.join(dir.path, "fr.o")}` +
|
||||
` ${path.join(dir.path, baseName + ".cpp")} ` +
|
||||
` -o ${path.join(dir.path, baseName)}` +
|
||||
|
||||
@@ -384,9 +384,9 @@ module.exports = function buildRuntime(module, builder) {
|
||||
"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.getLocal("cIdx"),
|
||||
c.getLocal("component"),
|
||||
c.getLocal("signal"),
|
||||
c.i32_const(0)
|
||||
)
|
||||
)
|
||||
@@ -412,6 +412,42 @@ module.exports = function buildRuntime(module, builder) {
|
||||
|
||||
}
|
||||
|
||||
function buildMultiGetSignal() {
|
||||
const f = module.addFunction("multiGetSignal");
|
||||
f.addParam("cIdx", "i32");
|
||||
f.addParam("pR", "i32");
|
||||
f.addParam("component", "i32");
|
||||
f.addParam("signal", "i32");
|
||||
f.addParam("n", "i32");
|
||||
f.addLocal("i", "i32");
|
||||
|
||||
const c = f.getCodeBuilder();
|
||||
|
||||
f.addCode(
|
||||
c.setLocal("i", c.i32_const(0)),
|
||||
c.block(c.loop(
|
||||
c.br_if(1, c.i32_eq(c.getLocal("i"), c.getLocal("n"))),
|
||||
c.call(
|
||||
"getSignal",
|
||||
c.getLocal("cIdx"),
|
||||
c.i32_add(
|
||||
c.getLocal("pR"),
|
||||
c.i32_mul(
|
||||
c.getLocal("i"),
|
||||
c.i32_const(builder.sizeFr)
|
||||
)
|
||||
),
|
||||
c.getLocal("component"),
|
||||
c.i32_add(
|
||||
c.getLocal("signal"),
|
||||
c.getLocal("i")
|
||||
)
|
||||
),
|
||||
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
||||
c.br(0)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
function buildSetSignal() {
|
||||
const f = module.addFunction("setSignal");
|
||||
@@ -473,7 +509,7 @@ module.exports = function buildRuntime(module, builder) {
|
||||
);
|
||||
|
||||
f.addCode(
|
||||
c.if( // If ( mapIsInput[s >> 5] & 1 << (s & 0x1f) )
|
||||
c.if( // If ( mapIsInput[s >> 5] & (1 << (s & 0x1f)) )
|
||||
c.i32_and(
|
||||
c.i32_load(
|
||||
c.i32_add(
|
||||
@@ -619,6 +655,35 @@ module.exports = function buildRuntime(module, builder) {
|
||||
));
|
||||
}
|
||||
|
||||
function buildCheckAssert() {
|
||||
const f = module.addFunction("checkAssert");
|
||||
f.addParam("cIdx", "i32");
|
||||
f.addParam("pA", "i32");
|
||||
f.addParam("pStr", "i32");
|
||||
|
||||
const c = f.getCodeBuilder();
|
||||
|
||||
f.addCode(ifSanityCheck(c,
|
||||
c.if (
|
||||
c.i32_eqz(
|
||||
c.call(
|
||||
"Fr_isTrue",
|
||||
c.getLocal("pA"),
|
||||
)
|
||||
),
|
||||
c.call(
|
||||
"error",
|
||||
c.i32_const(errs.ASSERT_DOES_NOT_MATCH.code),
|
||||
c.i32_const(errs.ASSERT_DOES_NOT_MATCH.pointer),
|
||||
c.getLocal("cIdx"),
|
||||
c.getLocal("pA"),
|
||||
c.getLocal("pStr"),
|
||||
c.i32_const(0)
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
function buildGetNVars() {
|
||||
const f = module.addFunction("getNVars");
|
||||
f.setReturnType("i32");
|
||||
@@ -628,6 +693,15 @@ module.exports = function buildRuntime(module, builder) {
|
||||
f.addCode(c.i32_const(builder.header.NVars));
|
||||
}
|
||||
|
||||
function buildGetNPublic() {
|
||||
const f = module.addFunction("getNPublic");
|
||||
f.setReturnType("i32");
|
||||
|
||||
const c = f.getCodeBuilder();
|
||||
|
||||
f.addCode(c.i32_const(builder.header.NPublic));
|
||||
}
|
||||
|
||||
function buildGetFrLen() {
|
||||
const f = module.addFunction("getFrLen");
|
||||
f.setReturnType("i32");
|
||||
@@ -818,13 +892,16 @@ module.exports = function buildRuntime(module, builder) {
|
||||
|
||||
buildGetSignal();
|
||||
buildSetSignal();
|
||||
buildMultiGetSignal();
|
||||
|
||||
buildComponentStarted();
|
||||
buildComponentFinished();
|
||||
|
||||
buildCheckConstraint();
|
||||
buildCheckAssert();
|
||||
|
||||
buildGetNVars();
|
||||
buildGetNPublic();
|
||||
buildGetFrLen();
|
||||
buildGetPWitness();
|
||||
buildGetPRawPrime();
|
||||
@@ -834,9 +911,11 @@ module.exports = function buildRuntime(module, builder) {
|
||||
|
||||
module.exportFunction("init");
|
||||
module.exportFunction("getNVars");
|
||||
module.exportFunction("getNPublic");
|
||||
module.exportFunction("getFrLen");
|
||||
module.exportFunction("getSignalOffset32");
|
||||
module.exportFunction("setSignal");
|
||||
module.exportFunction("multiGetSignal");
|
||||
module.exportFunction("getPWitness");
|
||||
module.exportFunction("Fr_toInt");
|
||||
module.exportFunction("getPRawPrime");
|
||||
|
||||
@@ -62,8 +62,9 @@ class CodeBuilderWasm {
|
||||
this.ops.push({op: "SETSIGNAL", component, signal, value});
|
||||
}
|
||||
|
||||
getSignal(dLabel, component, signal) {
|
||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
|
||||
getSignal(dLabel, component, signal, n) {
|
||||
if (typeof n == "undefined") n=1;
|
||||
this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n});
|
||||
}
|
||||
|
||||
copyN(dLabel, offset, src, n) {
|
||||
@@ -98,6 +99,9 @@ class CodeBuilderWasm {
|
||||
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
|
||||
}
|
||||
|
||||
checkAssert(a, strErr) {
|
||||
this.ops.push({op: "CHECKASSERT", a, strErr});
|
||||
}
|
||||
|
||||
concat(cb) {
|
||||
this.ops.push(...cb.ops);
|
||||
@@ -248,11 +252,12 @@ class CodeBuilderWasm {
|
||||
} else if (o.op == "GETSIGNAL") {
|
||||
code.push(
|
||||
c.call(
|
||||
"getSignal",
|
||||
"multiGetSignal",
|
||||
c.getLocal("cIdx"),
|
||||
this.fnBuilder._getPtr(c, o.dLabel),
|
||||
this.fnBuilder._deRefInt(c, o.component),
|
||||
this.fnBuilder._deRefInt(c, o.signal)
|
||||
this.fnBuilder._deRefInt(c, o.signal),
|
||||
c.i32_const(o.n)
|
||||
)
|
||||
);
|
||||
} else if (o.op == "COPYN") {
|
||||
@@ -332,6 +337,15 @@ class CodeBuilderWasm {
|
||||
c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
|
||||
)
|
||||
);
|
||||
} else if (o.op == "CHECKASSERT") {
|
||||
code.push(
|
||||
c.call(
|
||||
"checkAssert",
|
||||
c.getLocal("cIdx"),
|
||||
this.fnBuilder._deRefFr(c, o.a),
|
||||
c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
|
||||
)
|
||||
);
|
||||
} else if (o.op == "LOG") {
|
||||
code.push(
|
||||
c.call(
|
||||
@@ -767,20 +781,21 @@ class BuilderWasm {
|
||||
|
||||
_buildHeader(module) {
|
||||
|
||||
this.pCircuit = module.alloc(48);
|
||||
this.pCircuit = module.alloc(52);
|
||||
|
||||
this.pNSignals = this.pCircuit;
|
||||
this.pNComponents = this.pCircuit + 4;
|
||||
this.pNInputs = this.pCircuit + 8;
|
||||
this.pNOutputs = this.pCircuit + 12;
|
||||
this.pNVars = this.pCircuit + 16;
|
||||
this.ppWit2sig = this.pCircuit + 20;
|
||||
this.ppComponents = this.pCircuit + 24;
|
||||
this.ppMapIsInput = this.pCircuit + 28;
|
||||
this.ppConstants = this.pCircuit + 32;
|
||||
this.ppSignals = this.pCircuit + 36;
|
||||
this.ppInputSignalsToTrigger = this.pCircuit + 40;
|
||||
this.ppSignalsAssigned = this.pCircuit + 44;
|
||||
this.pNPublic = this.pCircuit + 20;
|
||||
this.ppWit2sig = this.pCircuit + 24;
|
||||
this.ppComponents = this.pCircuit + 28;
|
||||
this.ppMapIsInput = this.pCircuit + 32;
|
||||
this.ppConstants = this.pCircuit + 36;
|
||||
this.ppSignals = this.pCircuit + 40;
|
||||
this.ppInputSignalsToTrigger = this.pCircuit + 44;
|
||||
this.ppSignalsAssigned = this.pCircuit + 48;
|
||||
}
|
||||
|
||||
_buildSizes(module) {
|
||||
@@ -971,6 +986,7 @@ class BuilderWasm {
|
||||
module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
|
||||
module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
|
||||
module.addData(this.pNVars, intToBytes32(this.header.NVars));
|
||||
module.addData(this.pNPublic, intToBytes32(this.header.NPublic));
|
||||
module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
|
||||
module.addData(this.ppComponents, intToBytes32(this.pComponents));
|
||||
module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
|
||||
|
||||
@@ -7,4 +7,5 @@ module.exports = {
|
||||
SIGNAL_ASSIGNED_TWICE: {code: 6, str: "Signal assigned twice"},
|
||||
CONSTRAIN_DOES_NOT_MATCH: {code: 7, str: "Constraint doesn't match"},
|
||||
MAPISINPUT_DONT_MATCH: {code: 8, str: "MapIsInput don't match"},
|
||||
ASSERT_DOES_NOT_MATCH: {code: 9, str: "Assert not satisfied"},
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ function buildEntryTables(ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
ctx.builder.addComponentEntriesTable(componentEntriesTableName, componentEntriesTable);
|
||||
ctx.builder.addComponentEntriesTable(componentEntriesTableName, componentEntriesTable, i);
|
||||
|
||||
ctx.components[i].htName = htName;
|
||||
ctx.components[i].etName = componentEntriesTableName;
|
||||
@@ -196,6 +196,7 @@ function buildHeader(ctx) {
|
||||
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],
|
||||
NPublic: ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT],
|
||||
P: ctx.F.p
|
||||
});
|
||||
}
|
||||
|
||||
440
src/compiler.js
440
src/compiler.js
@@ -81,10 +81,6 @@ async function compile(srcFile, options) {
|
||||
throw new Error("A main component must be defined");
|
||||
}
|
||||
|
||||
if (ctx.verbose) console.log("Classify Signals");
|
||||
measures.classifySignals = -performance.now();
|
||||
classifySignals(ctx);
|
||||
measures.classifySignals += performance.now();
|
||||
|
||||
if (ctx.verbose) console.log("Reduce Constants");
|
||||
measures.reduceConstants = -performance.now();
|
||||
@@ -108,9 +104,13 @@ async function compile(srcFile, options) {
|
||||
measures.reduceConstraints += performance.now();
|
||||
|
||||
}
|
||||
|
||||
if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
|
||||
|
||||
if (ctx.verbose) console.log("Classify Signals");
|
||||
measures.classifySignals = -performance.now();
|
||||
classifySignals(ctx);
|
||||
measures.classifySignals += performance.now();
|
||||
|
||||
measures.generateWitnessNames = -performance.now();
|
||||
generateWitnessNames(ctx);
|
||||
measures.generateWitnessNames += performance.now();
|
||||
@@ -134,7 +134,7 @@ async function compile(srcFile, options) {
|
||||
measures.generateC = -performance.now();
|
||||
ctx.builder = new BuilderC(options.prime, ctx.verbose);
|
||||
build(ctx);
|
||||
await ctx.builder.build(options.cSourceFile);
|
||||
await ctx.builder.build(options.cSourceFile, options.dataFile);
|
||||
measures.generateC += performance.now();
|
||||
}
|
||||
|
||||
@@ -183,28 +183,36 @@ function classifySignals(ctx) {
|
||||
|
||||
function priorize(t1, t2) {
|
||||
if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
|
||||
if (t1 == ctx.stINTERNAL) {
|
||||
return t2;
|
||||
} else if (t2==ctx.stINTERNAL) {
|
||||
return t1;
|
||||
}
|
||||
if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
|
||||
if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
|
||||
if ((t1 == ctx.stPUBINPUT) || (t2 == ctx.stPUBINPUT)) return ctx.stPUBINPUT;
|
||||
if ((t1 == ctx.stPRVINPUT) || (t2 == ctx.stPRVINPUT)) return ctx.stPRVINPUT;
|
||||
if ((t1 == ctx.stINTERNAL) || (t2 == ctx.stINTERNAL)) return ctx.stINTERNAL;
|
||||
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;
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
if ((ctx.verbose)&&(i%100000 == 0)) console.log(`marking as internal: ${i}/${ctx.constraints.length}`);
|
||||
|
||||
const c = ctx.constraints[i];
|
||||
for (let s in c.a.coefs) ctx.signals[s].c = ctx.stINTERNAL;
|
||||
for (let s in c.b.coefs) ctx.signals[s].c = ctx.stINTERNAL;
|
||||
for (let s in c.c.coefs) ctx.signals[s].c = ctx.stINTERNAL;
|
||||
}
|
||||
|
||||
|
||||
// First classify the signals
|
||||
for (let s=0; s<ctx.signals.length; s++) {
|
||||
if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
|
||||
const signal = ctx.signals[s];
|
||||
let tAll = ctx.stINTERNAL;
|
||||
let tAll = ctx.stDISCARDED;
|
||||
let lSignal = signal;
|
||||
let end = false;
|
||||
while (!end) {
|
||||
let t = lSignal.c || ctx.stINTERNAL;
|
||||
let t = lSignal.c || ctx.stDISCARDED;
|
||||
if (s == 0) {
|
||||
t = ctx.stONE;
|
||||
} else if (lSignal.o & ctx.MAIN) {
|
||||
@@ -318,41 +326,53 @@ async function reduceConstrains(ctx) {
|
||||
const c = ctx.constraints[i];
|
||||
for (let s in c.a.coefs) {
|
||||
if (!insertedSig[s]) {
|
||||
if (!sig2constraint[s]) sig2constraint[s] = [];
|
||||
sig2constraint[s].push(i);
|
||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
||||
sig2constraint[s][i] = true;
|
||||
insertedSig[s] = true;
|
||||
}
|
||||
}
|
||||
for (let s in c.b.coefs) {
|
||||
if (!insertedSig[s]) {
|
||||
if (!sig2constraint[s]) sig2constraint[s] = [];
|
||||
sig2constraint[s].push(i);
|
||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
||||
sig2constraint[s][i] = true;
|
||||
insertedSig[s] = true;
|
||||
}
|
||||
}
|
||||
for (let s in c.c.coefs) {
|
||||
if (!insertedSig[s]) {
|
||||
if (!sig2constraint[s]) sig2constraint[s] = [];
|
||||
sig2constraint[s].push(i);
|
||||
if (!sig2constraint[s]) sig2constraint[s] = {};
|
||||
sig2constraint[s][i] = true;
|
||||
insertedSig[s] = true;
|
||||
}
|
||||
}
|
||||
possibleConstraints[i] = i;
|
||||
possibleConstraints[i] = ctx.constraints.length - i -1;
|
||||
}
|
||||
|
||||
let totalRemoved = 0;
|
||||
while (possibleConstraints.length >0) {
|
||||
nextPossibleConstraints = new BigArray();
|
||||
removedSignals = new BigArray();
|
||||
if (possibleConstraints.length>1<<20) {
|
||||
nextPossibleConstraints = new BigArray();
|
||||
} else {
|
||||
nextPossibleConstraints = {};
|
||||
}
|
||||
removedSignals = {};
|
||||
nRemoved = 0;
|
||||
lIdx = new BigArray();
|
||||
lIdx = {};
|
||||
for (let i=0;i<possibleConstraints.length;i++) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
||||
await Promise.resolve();
|
||||
console.log(`reducing constraints: ${i}/${possibleConstraints.length} reduced: ${nRemoved}`);
|
||||
}
|
||||
|
||||
const c = ctx.constraints[possibleConstraints[i]];
|
||||
if (!c) continue;
|
||||
|
||||
// Limit of number of lelements removed per step
|
||||
if (nRemoved>5000000) {
|
||||
nextPossibleConstraints[possibleConstraints[i]] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Swap a and b if b has more variables.
|
||||
if (Object.keys(c.b).length > Object.keys(c.a).length) {
|
||||
const aux = c.a;
|
||||
@@ -378,27 +398,37 @@ async function reduceConstrains(ctx) {
|
||||
const freeC = substituteRemoved(c.c);
|
||||
const isolatedSignal = getFirstInternalSignal(ctx, freeC);
|
||||
if (isolatedSignal) {
|
||||
// console.log(isolatedSignal);
|
||||
// console.log(freeC);
|
||||
removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
|
||||
if (lIdx[isolatedSignal]) {
|
||||
lIdx[isolatedSignal].forEach( (s) => {
|
||||
const sigs = Object.keys(lIdx[isolatedSignal]);
|
||||
|
||||
for (let k=0; k<sigs.length; k++) {
|
||||
const s = sigs[k];
|
||||
const oldLC = removedSignals[s];
|
||||
removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
|
||||
});
|
||||
if (oldLC !== removedSignals[s]) addTolIdx(removedSignals[s], s);
|
||||
}
|
||||
}
|
||||
|
||||
addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
|
||||
ctx.constraints[possibleConstraints[i]] = null;
|
||||
nRemoved ++;
|
||||
|
||||
sig2constraint[isolatedSignal].forEach( (s) => {
|
||||
nextPossibleConstraints[s] = true;
|
||||
});
|
||||
delete lIdx[isolatedSignal];
|
||||
|
||||
const cts = Object.keys(sig2constraint[isolatedSignal]);
|
||||
for (let k=0; k<cts.length; k++) {
|
||||
if (ctx.constraints[cts[k]]) nextPossibleConstraints[cts[k]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextPossibleConstraints = nextPossibleConstraints.getKeys();
|
||||
if (nextPossibleConstraints.getKeys) {
|
||||
nextPossibleConstraints = nextPossibleConstraints.getKeys();
|
||||
} else {
|
||||
nextPossibleConstraints = Object.keys(nextPossibleConstraints);
|
||||
}
|
||||
|
||||
for (let i=0; i<nextPossibleConstraints.length;i++) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
||||
@@ -408,9 +438,9 @@ async function reduceConstrains(ctx) {
|
||||
const c = ctx.constraints[nextPossibleConstraints[i]];
|
||||
if (c) {
|
||||
const nc = {
|
||||
a: substituteRemoved(c.a),
|
||||
b: substituteRemoved(c.b),
|
||||
c: substituteRemoved(c.c)
|
||||
a: substituteRemoved(c.a, nextPossibleConstraints[i]),
|
||||
b: substituteRemoved(c.b, nextPossibleConstraints[i]),
|
||||
c: substituteRemoved(c.c, nextPossibleConstraints[i])
|
||||
};
|
||||
if (ctx.lc.isZero(nc)) {
|
||||
delete ctx.constraints[nextPossibleConstraints[i]];
|
||||
@@ -420,7 +450,7 @@ async function reduceConstrains(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
const removedSignalsList = removedSignals.getKeys;
|
||||
const removedSignalsList = Object.keys(removedSignals);
|
||||
|
||||
for (let i=0; i<removedSignalsList.length; i++) {
|
||||
if ((ctx.verbose )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
|
||||
@@ -431,12 +461,23 @@ async function reduceConstrains(ctx) {
|
||||
lSignal = ctx.signals[lSignal.e];
|
||||
}
|
||||
|
||||
lSignal.c = ctx.stDISCARDED;
|
||||
sig2constraint[s] = null;
|
||||
}
|
||||
|
||||
/*
|
||||
possibleConstraints = new BigArray();
|
||||
// Reverse
|
||||
for (let i=0; i<nextPossibleConstraints.length; i++) {
|
||||
possibleConstraints[i] = nextPossibleConstraints[nextPossibleConstraints.length -1 -i];
|
||||
}
|
||||
*/
|
||||
possibleConstraints = nextPossibleConstraints;
|
||||
|
||||
totalRemoved += nRemoved;
|
||||
if (ctx.verbose) console.log(`Removed: ${totalRemoved} TotalConstraints: ${ctx.constraints.length}` );
|
||||
}
|
||||
|
||||
|
||||
let o=0;
|
||||
for (let i=0; i<ctx.constraints.length;i++) {
|
||||
if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
|
||||
@@ -454,7 +495,12 @@ async function reduceConstrains(ctx) {
|
||||
for (let k in l.coefs) {
|
||||
k = Number(k);
|
||||
const signal = ctx.signals[k];
|
||||
if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
|
||||
if ( ( ((signal.o & ctx.MAIN) == 0)
|
||||
||( ((signal.o & ctx.IN) == 0)
|
||||
&&((signal.o & ctx.OUT) == 0)))
|
||||
&&((signal.o & ctx.ONE) ==0)
|
||||
&&(!ctx.F.isZero(l.coefs[k]))
|
||||
&&(!removedSignals[k])) return k;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -476,7 +522,7 @@ async function reduceConstrains(ctx) {
|
||||
return eq;
|
||||
}
|
||||
|
||||
function substituteRemoved(lc) {
|
||||
function substituteRemoved(lc, idxConstraint) {
|
||||
const newLc = ctx.lc._clone(lc);
|
||||
for (let k in lc.coefs) {
|
||||
if (removedSignals[k]) {
|
||||
@@ -491,6 +537,10 @@ async function reduceConstrains(ctx) {
|
||||
newLc.coefs[k2] = newP;
|
||||
}
|
||||
}
|
||||
if ((typeof idxConstraint != "undefined")&&(k2!=0)) {
|
||||
if (!sig2constraint[k2]) sig2constraint[k2] = {};
|
||||
sig2constraint[k2][idxConstraint] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,318 +574,16 @@ async function reduceConstrains(ctx) {
|
||||
}
|
||||
|
||||
function addTolIdx(lc, newS) {
|
||||
for (let s in lc.coefs) {
|
||||
if (!lIdx[s]) lIdx[s] = [];
|
||||
lIdx[s].push(newS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function reduceConstrains_old(ctx) {
|
||||
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;
|
||||
const sigs = Object.keys(lc.coefs);
|
||||
for (let k=0; k<sigs.length; k++) {
|
||||
const s = sigs[k];
|
||||
if (s) {
|
||||
if (!lIdx[s]) lIdx[s] = {};
|
||||
lIdx[s][newS] = true;
|
||||
}
|
||||
|
||||
// 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 (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 = ctx.F.inv(c.c.coefs[isolatedSignal]);
|
||||
for (const s in c.c.coefs) {
|
||||
if (s != isolatedSignal) {
|
||||
const v = ctx.F.mul( ctx.F.neg(c.c.coefs[s]), invCoef);
|
||||
if (!ctx.F.isZero(v)) {
|
||||
isolatedSignalEquivalence.coefs[s] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
possibleConstraints = 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.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.coefs) {
|
||||
const signal = ctx.signals[k];
|
||||
if (signal.c == ctx.stINTERNAL) return k;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isConstant(l) {
|
||||
for (let k in l.coefs) {
|
||||
if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
|
||||
}
|
||||
if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
function buildCircuitDef(ctx, mainCode) {
|
||||
const res = {
|
||||
mainCode: mainCode
|
||||
};
|
||||
res.signalName2Idx = ctx.signalName2Idx;
|
||||
|
||||
res.components = [];
|
||||
res.componentName2Idx = {};
|
||||
for (let c in ctx.components) {
|
||||
const idCoponent = res.components.length;
|
||||
res.components.push({
|
||||
name: c,
|
||||
params: ctx.components[c].params,
|
||||
template: ctx.components[c].template,
|
||||
inputSignals: 0
|
||||
});
|
||||
res.componentName2Idx[c] = idCoponent;
|
||||
}
|
||||
|
||||
res.signals = new Array(ctx.signalNames.length);
|
||||
for (let i=0; i<ctx.signalNames.length; i++) {
|
||||
res.signals[i] = {
|
||||
names: ctx.signalNames[i],
|
||||
triggerComponents: []
|
||||
};
|
||||
ctx.signalNames[i].map( (fullName) => {
|
||||
const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
|
||||
if (ctx.signals[fullName].direction == "IN") {
|
||||
res.signals[i].triggerComponents.push(idComponet);
|
||||
res.components[idComponet].inputSignals++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
res.constraints = buildConstraints(ctx);
|
||||
|
||||
res.templates = ctx.templates;
|
||||
|
||||
res.functions = {};
|
||||
for (let f in ctx.functions) {
|
||||
res.functions[f] = {
|
||||
params: ctx.functionParams[f],
|
||||
func: ctx.functions[f]
|
||||
};
|
||||
}
|
||||
|
||||
res.nPrvInputs = ctx.totals.prvInput;
|
||||
res.nPubInputs = ctx.totals.pubInput;
|
||||
res.nInputs = res.nPrvInputs + res.nPubInputs;
|
||||
res.nOutputs = ctx.totals.output;
|
||||
res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
|
||||
res.nConstants = ctx.totals.constant;
|
||||
res.nSignals = res.nVars + res.nConstants;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Build constraints
|
||||
|
||||
A constraint like this
|
||||
|
||||
[s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0 ] = 0
|
||||
[ 5*s2 + 6*s3] * [ s2 + ] - [s0 + 2* s2] = 0
|
||||
[s1 + s3] * [ s2 + 5*s3] - [s4 ] = 0
|
||||
|
||||
is converted to
|
||||
|
||||
[
|
||||
[{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1" }],
|
||||
[{ "2":"5","3":"6"} , {"2":"1" } , {"0":"1", "2":"2"}],
|
||||
[{"1":"1", "3":"1"} , {"2":"1","3":"5"} , {"4":"1" }]
|
||||
]
|
||||
^ ^ ^
|
||||
| | |
|
||||
A B C
|
||||
|
||||
*/
|
||||
/*
|
||||
function buildConstraints(ctx) {
|
||||
const res = [];
|
||||
|
||||
function fillLC(dst, src) {
|
||||
if (src.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;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i=0; i<ctx.constraints.length; i++) {
|
||||
const A = {};
|
||||
const B = {};
|
||||
const C = {};
|
||||
|
||||
fillLC(A, ctx.constraints[i].a);
|
||||
fillLC(B, ctx.constraints[i].b);
|
||||
fillLC(C, 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@@ -428,10 +428,17 @@ function execAssignement(ctx, ast) {
|
||||
}
|
||||
|
||||
// Skip if an out is assigned directly to an input.
|
||||
if ((!isIn)||(!isOut)) {
|
||||
sDest.e = sIdx;
|
||||
} else {
|
||||
if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
|
||||
if (!(isIn&&isOut)) {
|
||||
if (isIn) {
|
||||
sDest.e = sIdx;
|
||||
} else if (isOut) {
|
||||
sSrc.e = dIdx;
|
||||
} else {
|
||||
sDest.e = sIdx;
|
||||
}
|
||||
if (!isOut) {
|
||||
if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -567,9 +574,13 @@ function execFunctionCall(ctx, ast) {
|
||||
return;
|
||||
}
|
||||
if (ast.name == "assert") {
|
||||
ast.fileName = ctx.fileName;
|
||||
ast.filePath = ctx.filePath;
|
||||
|
||||
const v = exec(ctx, ast.params[0]);
|
||||
const ev = val(ctx, v, ast);
|
||||
if (ctx.F.isZero(ev)) return ctx.throwError(ast, "Assertion failed");
|
||||
if ((typeof ev.v !== "undefined")&&(ctx.F.isZero(ev.v))) return ctx.throwError(ast, "Assertion failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const fnc = ctx.functions[ast.name];
|
||||
@@ -697,6 +708,9 @@ function execPin(ctx, ast) {
|
||||
if (sel.v[0].t != "N") return NQVAL;
|
||||
selsP.push(Scalar.toNumber(sel.v[0].v));
|
||||
}
|
||||
if (!ctx.components[cIdx]) {
|
||||
return ctx.throwError(ast, "Component not defined yet");
|
||||
}
|
||||
const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
|
||||
|
||||
if (sIdx<0) return ctx.throwError(ast, "Signal not defined:" + buildFullName() );
|
||||
@@ -874,7 +888,8 @@ function execOpOp(ctx, ast, op, lr) {
|
||||
right = {t:"N", v: ctx.F.one};
|
||||
}
|
||||
|
||||
if (!right) return ctx.throwError(ast, "adding a no number");
|
||||
if (!right) return ctx.throwError(ast, "right operand is not initialized");
|
||||
if (!resBefore) return ctx.throwError(ast, "left operand is not initialized");
|
||||
|
||||
const resAfter = ctx.lc[op](resBefore, right);
|
||||
left.v[o] = resAfter;
|
||||
|
||||
@@ -284,6 +284,32 @@ function genDeclareComponent(ctx, ast) {
|
||||
}
|
||||
|
||||
function genDeclareSignal(ctx, ast) {
|
||||
|
||||
const v = ctx.refs[ast.refId];
|
||||
|
||||
let sizes;
|
||||
if (ast.name.selectors.length>0) {
|
||||
sizes=[];
|
||||
for (let i=0; i< ast.name.selectors.length; i++) {
|
||||
const sizeRef = gen(ctx, ast.name.selectors[i]);
|
||||
const size = ctx.refs[sizeRef];
|
||||
if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array");
|
||||
if (size.used) return ctx.throwError(ast, "Signal size variables not allowed");
|
||||
sizes.push(Scalar.toNumber(size.value[0]));
|
||||
}
|
||||
sizes = utils.accSizes(sizes);
|
||||
} else {
|
||||
sizes = [1,0];
|
||||
}
|
||||
|
||||
if ((!v.sizes)&&(sizes)) {
|
||||
v.sizes = sizes;
|
||||
}
|
||||
|
||||
if (v.sizes) {
|
||||
if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a signal with different sized");
|
||||
}
|
||||
|
||||
return ast.refId;
|
||||
}
|
||||
|
||||
@@ -414,12 +440,13 @@ function genVariable(ctx, ast) {
|
||||
vOffset = genGetSignalOffset(ctx, -1, ast.name);
|
||||
}
|
||||
|
||||
const resRef = newRef(ctx, "BIGINT", "_sigValue");
|
||||
const sizes = v.sizes.slice(l);
|
||||
|
||||
const resRef = newRef(ctx, "BIGINT", "_sigValue", null, sizes);
|
||||
const res = ctx.refs[resRef];
|
||||
instantiateRef(ctx, resRef);
|
||||
ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset));
|
||||
ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset), sizes[0]);
|
||||
return resRef;
|
||||
|
||||
} else if (v.type == "BIGINT") {
|
||||
const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors );
|
||||
const offset = ctx.refs[refOffset];
|
||||
@@ -486,13 +513,18 @@ function genPin(ctx, ast) {
|
||||
vsIdx = genGetSignalOffset(ctx, vcIdx, ast.pin.name);
|
||||
}
|
||||
|
||||
const resRef = newRef(ctx, "BIGINT", "_sigValue");
|
||||
const l = ast.selectors ? ast.selectors.length : 0;
|
||||
|
||||
const sizes = [1,0]; // TODO, Treat array
|
||||
|
||||
const resRef = newRef(ctx, "BIGINT", "_sigValue", null, sizes);
|
||||
const res = ctx.refs[resRef];
|
||||
instantiateRef(ctx, resRef);
|
||||
ctx.codeBuilder.getSignal(
|
||||
res.label,
|
||||
toRefA_Int1(ctx, ast.component, vcIdx),
|
||||
toRefA_Int1(ctx, ast.pin, vsIdx)
|
||||
toRefA_Int1(ctx, ast.pin, vsIdx),
|
||||
sizes[0]
|
||||
);
|
||||
return resRef;
|
||||
}
|
||||
@@ -723,7 +755,15 @@ function genVarAssignment(ctx, ast, lRef, sels, rRef) {
|
||||
|
||||
if (instantiated) {
|
||||
if (offset.used) {
|
||||
ctx.codeBuilder.copyN(left.label, ["R", offset.label], ["R", right.label], right.sizes[0]);
|
||||
let ot;
|
||||
if (offset.type == "BIGINT") {
|
||||
ot = "R";
|
||||
} else if (offset.type == "INT") {
|
||||
ot= "RI";
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
ctx.codeBuilder.copyN(left.label, [ot, offset.label], ["R", right.label], right.sizes[0]);
|
||||
} else {
|
||||
ctx.codeBuilder.copyN(left.label, ["V", offset.value[0]], ["R", right.label], right.sizes[0]);
|
||||
}
|
||||
@@ -863,6 +903,12 @@ function genFunctionCall(ctx, ast) {
|
||||
ctx.codeBuilder.log(toRefA_Fr1(ctx, ast.params[0], vRef));
|
||||
return vRef;
|
||||
}
|
||||
if (ast.name == "assert") {
|
||||
const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
|
||||
const vRef = gen(ctx, ast.params[0]);
|
||||
ctx.codeBuilder.checkAssert(toRefA_Fr1(ctx, ast.params[0], vRef), strErr);
|
||||
return vRef;
|
||||
}
|
||||
const params = [];
|
||||
for (let i=0; i<ast.params.length; i++) {
|
||||
const pRef = gen(ctx, ast.params[i]);
|
||||
@@ -937,7 +983,7 @@ function genLoop(ctx, ast) {
|
||||
if (ctx.error) return;
|
||||
|
||||
const cond = ctx.refs[condRef];
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genLoop: Operation cannot be done on an array");
|
||||
|
||||
if (cond.used) {
|
||||
inLoop = true;
|
||||
@@ -1014,7 +1060,7 @@ function genIf(ctx, ast) {
|
||||
const condRef = gen(ctx, ast.condition);
|
||||
if (ctx.error) return;
|
||||
const cond = ctx.refs[condRef];
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genIf: Operation cannot be done on an array");
|
||||
|
||||
if (cond.used) {
|
||||
let thenCode, elseCode;
|
||||
@@ -1134,7 +1180,12 @@ function genOp(ctx, ast, op, nOps, adjustBool) {
|
||||
valRefs.push(ref);
|
||||
vals.push(v);
|
||||
|
||||
if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array");
|
||||
if (!utils.sameSizes(v.sizes, [1,0])) {
|
||||
console.log("xx");
|
||||
console.log(i);
|
||||
console.log(v);
|
||||
return ctx.throwError(ast, "genOp2: Operation cannot be done on an array");
|
||||
}
|
||||
if ( (!v.used)
|
||||
&&( (!utils.isDefined(v.value))
|
||||
||(!utils.isDefined(v.value[0]))))
|
||||
@@ -1170,7 +1221,7 @@ function genTerCon(ctx, ast) {
|
||||
const condRef = gen(ctx, ast.values[0]);
|
||||
if (ctx.error) return;
|
||||
const cond = ctx.refs[condRef];
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
|
||||
if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genTer: Operation cannot be done on an array");
|
||||
|
||||
if (cond.used) {
|
||||
let thenCode, elseCode;
|
||||
|
||||
@@ -8,8 +8,7 @@ module.exports.buildR1cs = buildR1cs;
|
||||
|
||||
async function buildR1cs(ctx, fileName) {
|
||||
|
||||
const fd = await fastFile.createOverride(fileName);
|
||||
|
||||
const fd = await fastFile.createOverride(fileName, 1<<24, 1<<22);
|
||||
|
||||
const buffBigInt = new Uint8Array(ctx.F.n8);
|
||||
|
||||
@@ -59,7 +58,7 @@ async function buildR1cs(ctx, fileName) {
|
||||
if ((ctx.verbose)&&(i%10000 == 0)) {
|
||||
if (ctx.verbose) console.log("writing constraint: ", i);
|
||||
}
|
||||
await writeConstraint(ctx.constraints[i]);
|
||||
await writeConstraint(ctx.constraints[i], i);
|
||||
}
|
||||
|
||||
const constraintsSize = fd.pos - pConstraintsSize - 8;
|
||||
@@ -89,13 +88,13 @@ async function buildR1cs(ctx, fileName) {
|
||||
const mapSize = fd.pos - pMapSize - 8;
|
||||
|
||||
// Write sizes
|
||||
await fd.writeULE32(headerSize, pHeaderSize);
|
||||
await fd.writeULE32(constraintsSize, pConstraintsSize);
|
||||
await fd.writeULE32(mapSize, pMapSize);
|
||||
await fd.writeULE64(headerSize, pHeaderSize);
|
||||
await fd.writeULE64(constraintsSize, pConstraintsSize);
|
||||
await fd.writeULE64(mapSize, pMapSize);
|
||||
|
||||
await fd.close();
|
||||
|
||||
function writeConstraint(c) {
|
||||
function writeConstraint(c, ctIdx) {
|
||||
const n8 = ctx.F.n8;
|
||||
const idxA = Object.keys(c.a.coefs);
|
||||
const idxB = Object.keys(c.b.coefs);
|
||||
@@ -109,6 +108,7 @@ async function buildR1cs(ctx, fileName) {
|
||||
const coef = idxA[i];
|
||||
let lSignal = ctx.signals[coef];
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
if (lSignal.id >= NWires) throw new Error("Signal Coef A: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
||||
ctx.F.toRprLE(buff, o, c.a.coefs[coef]); o+=n8;
|
||||
}
|
||||
@@ -118,8 +118,8 @@ async function buildR1cs(ctx, fileName) {
|
||||
const coef = idxB[i];
|
||||
let lSignal = ctx.signals[coef];
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
if (lSignal.id >= NWires) throw new Error("Signal Coef B: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
||||
|
||||
ctx.F.toRprLE(buff, o, c.b.coefs[coef]); o+=n8;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ async function buildR1cs(ctx, fileName) {
|
||||
const coef = idxC[i];
|
||||
let lSignal = ctx.signals[coef];
|
||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||
if (lSignal.id >= NWires) throw new Error("Signal Coef C: " + coef + " Constraint: " + ctIdx + " id: " + lSignal.id);
|
||||
buffV.setUint32(o, lSignal.id, true); o+=4;
|
||||
ctx.F.toRprLE(buff, o, ctx.F.neg(c.c.coefs[coef])); o+=n8;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ module.exports.fnvHash = fnvHash;
|
||||
module.exports.sameSizes = sameSizes;
|
||||
module.exports.isDefined = isDefined;
|
||||
module.exports.accSizes2Str = accSizes2Str;
|
||||
module.exports.setUint64 = setUint64;
|
||||
|
||||
function ident(text) {
|
||||
if (typeof text === "string") {
|
||||
@@ -90,6 +91,14 @@ function accSizes2Str(sizes) {
|
||||
return `[${sizes[0]/sizes[1]}]`+accSizes2Str(sizes.slice(1));
|
||||
}
|
||||
|
||||
function setUint64(buffV, o, n) {
|
||||
const sLSB = n >>> 0;
|
||||
const sMSB = (n - sLSB) / 0x100000000;
|
||||
buffV.setUint32(o, sLSB , true);
|
||||
buffV.setUint32(o+4, sMSB , true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ async function doTest(tester, circuit, testVectors) {
|
||||
describe("basic cases", function () {
|
||||
this.timeout(100000);
|
||||
|
||||
for (let i=0; i<basicCases.length; i++) {
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fnarray",
|
||||
"circuit": "fnarray.circom",
|
||||
"tv": [
|
||||
[{
|
||||
"in": [1, 8, 25]
|
||||
}, {
|
||||
"out": [2, 16, 50]
|
||||
}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "add",
|
||||
"circuit": "add.circom",
|
||||
|
||||
21
test/circuits/fnarray.circom
Normal file
21
test/circuits/fnarray.circom
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
|
||||
|
||||
function calc(h) {
|
||||
var res[3];
|
||||
for (var i=0; i<3; i++) res[i] = h[i]*2;
|
||||
return res;
|
||||
}
|
||||
|
||||
template Test() {
|
||||
signal input in[3];
|
||||
signal output out[3];
|
||||
|
||||
var cout[3] = calc(in);
|
||||
for (var i=0; i<3; i++) out[i] <-- cout[i];
|
||||
|
||||
for (var i=0; i<3; i++) out[i] === in[i]*2;
|
||||
}
|
||||
|
||||
component main = Test();
|
||||
Reference in New Issue
Block a user