circom/src/compiler.js
src/compiler.jsRaw
1/*
2 Copyright 2018 0KIMS association.
3
4 This file is part of circom (Zero Knowledge Circuit Compiler).
5
6 circom is a free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 circom is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with circom. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20const Scalar = require("ffjavascript").Scalar;
21const sONE = 0;
22const build = require("./build");
23const BuilderC = require("../ports/c/builder.js");
24const BuilderWasm = require("../ports/wasm/builder.js");
25const constructionPhase = require("./construction_phase");
26const Ctx = require("./ctx");
27const utils = require("./utils");
28const buildR1cs = require("./r1csfile").buildR1cs;
29const BigArray = require("./bigarray");
30const buildSyms = require("./buildsyms");
31const {performance} = require("perf_hooks");
32
33module.exports = compile;
34const measures = {};
35
36function ms2String(v) {
37 v = Math.floor(v);
38 const ms = v % 1000;
39 v = Math.floor(v/1000);
40 const secs = v % 60;
41 v = Math.floor(v/60);
42 const mins = v % 60;
43 v = Math.floor(v/60);
44 const hours = v % 24;
45 const days = Math.floor(v/24);
46 let S = "";
47 if (days) S = S + days + "D ";
48 if ((S!="")||(hours)) S = S + hours.toString().padStart(2, "0") + ":";
49 if ((S!="")||(mins)) S = S + mins.toString().padStart(2, "0") + ":";
50 if ((S!="")||(secs)) S = S + secs.toString().padStart(2, "0");
51 S+=".";
52 S = S + ms.toString().padStart(3, "0");
53 return S;
54}
55
56async function compile(srcFile, options) {
57 options.prime = options.prime || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
58 if (!options) {
59 options = {};
60 }
61 if (typeof options.reduceConstraints === "undefined") {
62 options.reduceConstraints = true;
63 }
64 const ctx = new Ctx(options.prime);
65 ctx.verbose= options.verbose || false;
66 ctx.mainComponent = options.mainComponent || "main";
67 ctx.newThreadTemplates = options.newThreadTemplates;
68
69 measures.constructionPhase = -performance.now();
70 constructionPhase(ctx, srcFile);
71 measures.constructionPhase += performance.now();
72
73 if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length);
74 if (ctx.verbose) console.log("NSignals Before: "+ctx.signals.length);
75
76 if (ctx.error) {
77 throw(ctx.error);
78 }
79
80 if (ctx.getComponentIdx(ctx.mainComponent)<0) {
81 throw new Error("A main component must be defined");
82 }
83
84 if (ctx.verbose) console.log("Classify Signals");
85 measures.classifySignals = -performance.now();
86 classifySignals(ctx);
87 measures.classifySignals += performance.now();
88
89 if (ctx.verbose) console.log("Reduce Constants");
90 measures.reduceConstants = -performance.now();
91 reduceConstants(ctx);
92 measures.reduceConstants += performance.now();
93
94 if (options.reduceConstraints) {
95
96 if (ctx.verbose) console.log("Reduce Constraints");
97 // Repeat while reductions are performed
98/*
99 let oldNConstrains = -1;
100 while (ctx.constraints.length != oldNConstrains) {
101 if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length);
102 oldNConstrains = ctx.constraints.length;
103 reduceConstrains(ctx);
104 }
105*/
106 measures.reduceConstraints = -performance.now();
107 await reduceConstrains(ctx);
108 measures.reduceConstraints += performance.now();
109
110 }
111
112 if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
113
114 measures.generateWitnessNames = -performance.now();
115 generateWitnessNames(ctx);
116 measures.generateWitnessNames += performance.now();
117
118 if (ctx.error) {
119 throw(ctx.error);
120 }
121
122 if (options.r1csFileName) {
123 measures.generateR1cs = -performance.now();
124 await buildR1cs(ctx, options.r1csFileName);
125 measures.generateR1cs += performance.now();
126 }
127
128 if (ctx.error) throw(ctx.error);
129
130 delete ctx.constraints; // Liberate memory.
131
132 if (options.cSourceWriteStream) {
133 if (ctx.verbose) console.log("Generating c...");
134 measures.generateC = -performance.now();
135 ctx.builder = new BuilderC(options.prime);
136 build(ctx);
137 const rdStream = ctx.builder.build();
138 rdStream.pipe(options.cSourceWriteStream);
139 measures.generateC += performance.now();
140
141 // await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
142 }
143
144 if (ctx.error) throw(ctx.error);
145
146 if ((options.wasmWriteStream)||(options.watWriteStream)) {
147 if (ctx.verbose) console.log("Generating wasm...");
148 measures.generateWasm = -performance.now();
149 ctx.builder = new BuilderWasm(options.prime);
150 build(ctx);
151 if (options.wasmWriteStream) {
152 const rdStream = ctx.builder.build("wasm");
153 rdStream.pipe(options.wasmWriteStream);
154 }
155 if (options.watWriteStream) {
156 const rdStream = ctx.builder.build("wat");
157 rdStream.pipe(options.watWriteStream);
158 }
159 measures.generateWasm += performance.now();
160
161 // await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
162 }
163
164 // const mainCode = gen(ctx,ast);
165 if (ctx.error) throw(ctx.error);
166
167 if (options.symWriteStream) {
168 measures.generateSyms = -performance.now();
169 const rdStream = buildSyms(ctx);
170 rdStream.pipe(options.symWriteStream);
171 measures.generateSyms += performance.now();
172
173 await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
174 }
175
176// const def = buildCircuitDef(ctx, mainCode);
177
178 if (ctx.verbose) {
179 for (let [mStr, mValue] of Object.entries(measures)) {
180 console.log(mStr + ": " + ms2String(mValue));
181 }
182 }
183}
184
185
186
187function classifySignals(ctx) {
188
189 const ERROR = 0xFFFF;
190
191 function priorize(t1, t2) {
192 if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
193 if (t1 == ctx.stINTERNAL) {
194 return t2;
195 } else if (t2==ctx.stINTERNAL) {
196 return t1;
197 }
198 if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
199 if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
200 if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
201 if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
202 if (t1!=t2) return ERROR;
203 return t1;
204 }
205
206 // First classify the signals
207 for (let s=0; s<ctx.signals.length; s++) {
208 if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
209 const signal = ctx.signals[s];
210 let tAll = ctx.stINTERNAL;
211 let lSignal = signal;
212 let end = false;
213 while (!end) {
214 let t = lSignal.c || ctx.stINTERNAL;
215 if (s == 0) {
216 t = ctx.stONE;
217 } else if (lSignal.o & ctx.MAIN) {
218 if (lSignal.o & ctx.IN) {
219 if (lSignal.o & ctx.PRV) {
220 t = ctx.stPRVINPUT;
221 } else {
222 t = ctx.stPUBINPUT;
223 }
224 } else if (lSignal.o & ctx.OUT) {
225 t = ctx.stOUTPUT;
226 }
227 } else if (utils.isDefined(lSignal.v)) {
228 t = ctx.stCONSTANT;
229 }
230 tAll = priorize(t,tAll);
231 if (lSignal.e>=0) {
232 lSignal = ctx.signals[lSignal.e];
233 } else {
234 end=true;
235 }
236 }
237 if (tAll == ERROR) {
238 throw new Error("Incompatible types in signal: " + s);
239 }
240 lSignal.c = tAll;
241 }
242}
243
244
245function generateWitnessNames(ctx) {
246 const totals = {};
247 totals[ctx.stONE] = 0;
248 totals[ctx.stOUTPUT] = 0;
249 totals[ctx.stPUBINPUT] = 0;
250 totals[ctx.stPRVINPUT] = 0;
251 totals[ctx.stINTERNAL] = 0;
252 totals[ctx.stDISCARDED] = 0;
253 totals[ctx.stCONSTANT] = 0;
254 const ids = {};
255
256 // First classify the signals
257 for (let s=0; s<ctx.signals.length; s++) {
258
259 if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
260
261 const signal = ctx.signals[s];
262 let lSignal = signal;
263 while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
264
265 if (!( lSignal.o & ctx.COUNTED) ) {
266 lSignal.o |= ctx.COUNTED;
267 totals[lSignal.c] ++;
268 }
269 }
270
271 ids[ctx.stONE] = 0;
272 ids[ctx.stOUTPUT] = 1;
273 ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
274 ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
275 ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
276 ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
277 ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
278 const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
279
280 for (let s=0; s<ctx.signals.length; s++) {
281
282 if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
283
284 const signal = ctx.signals[s];
285 let lSignal = signal;
286 while (lSignal.e>=0) {
287 lSignal = ctx.signals[lSignal.e];
288 }
289 if ( typeof(lSignal.id) === "undefined" ) {
290 lSignal.id = ids[lSignal.c] ++;
291 }
292
293 signal.id = lSignal.id;
294 }
295
296 ctx.totals = totals;
297}
298
299function reduceConstants(ctx) {
300 const newConstraints = new BigArray();
301 for (let i=0; i<ctx.constraints.length; i++) {
302 if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
303 const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
304 if (!ctx.lc.isZero(c)) {
305 newConstraints.push(c);
306 }
307 delete ctx.constraints[i];
308 }
309 ctx.constraints = newConstraints;
310}
311
312async function reduceConstrains(ctx) {
313 const sig2constraint = new BigArray();
314 let removedSignals = new BigArray();
315 let nRemoved;
316 let lIdx;
317
318
319 let possibleConstraints = new BigArray(ctx.constraints.length);
320 let nextPossibleConstraints;
321 for (let i=0; i<ctx.constraints.length; i++) {
322 if ((ctx.verbose)&&(i%100000 == 0)) console.log(`indexing constraints: ${i}/${ctx.constraints.length}`);
323
324 const insertedSig = { 0: true}; // Do not insert one.
325 const c = ctx.constraints[i];
326 for (let s in c.a.coefs) {
327 if (!insertedSig[s]) {
328 if (!sig2constraint[s]) sig2constraint[s] = [];
329 sig2constraint[s].push(i);
330 insertedSig[s] = true;
331 }
332 }
333 for (let s in c.b.coefs) {
334 if (!insertedSig[s]) {
335 if (!sig2constraint[s]) sig2constraint[s] = [];
336 sig2constraint[s].push(i);
337 insertedSig[s] = true;
338 }
339 }
340 for (let s in c.c.coefs) {
341 if (!insertedSig[s]) {
342 if (!sig2constraint[s]) sig2constraint[s] = [];
343 sig2constraint[s].push(i);
344 insertedSig[s] = true;
345 }
346 }
347 possibleConstraints[i] = i;
348 }
349
350 while (possibleConstraints.length >0) {
351 nextPossibleConstraints = new BigArray();
352 removedSignals = new BigArray();
353 nRemoved = 0;
354 lIdx = new BigArray();
355 for (let i=0;i<possibleConstraints.length;i++) {
356 if ((ctx.verbose)&&(i%10000 == 0)) {
357 await Promise.resolve();
358 console.log(`reducing constraints: ${i}/${possibleConstraints.length} reduced: ${nRemoved}`);
359 }
360 const c = ctx.constraints[possibleConstraints[i]];
361 if (!c) continue;
362
363 // Swap a and b if b has more variables.
364 if (Object.keys(c.b).length > Object.keys(c.a).length) {
365 const aux = c.a;
366 c.a=c.b;
367 c.b=aux;
368 }
369
370 // Mov to C if possible.
371 if (isConstant(c.a)) {
372 const ct = {t: "N", v: c.a.coefs[sONE]};
373 c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
374 c.a = { t: "LC", coefs: {} };
375 c.b = { t: "LC", coefs: {} };
376 }
377 if (isConstant(c.b)) {
378 const ct = {t: "N", v: c.b.coefs[sONE]};
379 c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
380 c.a = { t: "LC", coefs: {} };
381 c.b = { t: "LC", coefs: {} };
382 }
383
384 if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
385 const freeC = substituteRemoved(c.c);
386 const isolatedSignal = getFirstInternalSignal(ctx, freeC);
387 if (isolatedSignal) {
388 // console.log(isolatedSignal);
389 // console.log(freeC);
390 removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
391 if (lIdx[isolatedSignal]) {
392 lIdx[isolatedSignal].forEach( (s) => {
393 removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
394 });
395 }
396
397 addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
398 ctx.constraints[possibleConstraints[i]] = null;
399 nRemoved ++;
400
401 sig2constraint[isolatedSignal].forEach( (s) => {
402 nextPossibleConstraints[s] = true;
403 });
404 }
405 }
406 }
407
408 nextPossibleConstraints = nextPossibleConstraints.getKeys();
409
410 for (let i=0; i<nextPossibleConstraints.length;i++) {
411 if ((ctx.verbose)&&(i%10000 == 0)) {
412 await Promise.resolve();
413 console.log(`substituting constraints: ${i}/${nextPossibleConstraints.length}`);
414 }
415 const c = ctx.constraints[nextPossibleConstraints[i]];
416 if (c) {
417 const nc = {
418 a: substituteRemoved(c.a),
419 b: substituteRemoved(c.b),
420 c: substituteRemoved(c.c)
421 };
422 if (ctx.lc.isZero(nc)) {
423 delete ctx.constraints[nextPossibleConstraints[i]];
424 } else {
425 ctx.constraints[nextPossibleConstraints[i]] = nc;
426 }
427 }
428 }
429
430 const removedSignalsList = removedSignals.getKeys;
431
432 for (let i=0; i<removedSignalsList.length; i++) {
433 if ((ctx.verbose )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
434 const s = removedSignalsList[i];
435
436 let lSignal = ctx.signals[s];
437 while (lSignal.e>=0) {
438 lSignal = ctx.signals[lSignal.e];
439 }
440
441 lSignal.c = ctx.stDISCARDED;
442 }
443
444 possibleConstraints = nextPossibleConstraints;
445 }
446
447 let o=0;
448 for (let i=0; i<ctx.constraints.length;i++) {
449 if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
450 if (ctx.constraints[i]) {
451 if (!ctx.lc.isZero(ctx.constraints[i])) {
452 ctx.constraints[o] = ctx.constraints[i];
453 o++;
454 }
455 }
456 }
457
458 ctx.constraints.length = o;
459
460 function getFirstInternalSignal(ctx, l) {
461 for (let k in l.coefs) {
462 k = Number(k);
463 const signal = ctx.signals[k];
464 if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
465 }
466 return null;
467 }
468
469 function isolateSignal(lc, s) {
470 const eq = {
471 t: "LC",
472 coefs: {}
473 };
474 const invCoef = ctx.F.inv(lc.coefs[s]);
475 for (const k in lc.coefs) {
476 if (k != s) {
477 const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef);
478 if (!ctx.F.isZero(v)) {
479 eq.coefs[k] = v;
480 }
481 }
482 }
483 return eq;
484 }
485
486 function substituteRemoved(lc) {
487 const newLc = ctx.lc._clone(lc);
488 for (let k in lc.coefs) {
489 if (removedSignals[k]) {
490 delete newLc.coefs[k];
491 for (let k2 in removedSignals[k].coefs) {
492 const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]);
493 if (!ctx.F.isZero(newP)) {
494 if (newLc.coefs[k2]) {
495 newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
496 if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
497 } else {
498 newLc.coefs[k2] = newP;
499 }
500 }
501 }
502 }
503 }
504 return newLc;
505 }
506
507 function substitute(lc, s, eq) {
508 if (!lc.coefs[s]) return lc;
509 const newLc = ctx.lc._clone(lc);
510 delete newLc.coefs[s];
511 for (let k2 in eq.coefs) {
512 const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]);
513 if (!ctx.F.isZero(newP)) {
514 if (newLc.coefs[k2]) {
515 newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
516 if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
517 } else {
518 newLc.coefs[k2] = newP;
519 }
520 }
521 }
522 return newLc;
523 }
524
525 function isConstant(l) {
526 for (let k in l.coefs) {
527 if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
528 }
529 if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
530 return true;
531 }
532
533 function addTolIdx(lc, newS) {
534 for (let s in lc.coefs) {
535 if (!lIdx[s]) lIdx[s] = [];
536 lIdx[s].push(newS);
537 }
538 }
539
540
541}
542
543
544function reduceConstrains_old(ctx) {
545 indexVariables();
546 let possibleConstraints = ctx.constraints;
547 let ii=0;
548 while (possibleConstraints.length>0) {
549 let nextPossibleConstraints = new BigArray();
550 for (let i=0; i<possibleConstraints.length; i++) {
551 ii++;
552 if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
553 if (!ctx.constraints[i]) continue;
554 const c = ctx.constraints[i];
555
556 // Swap a and b if b has more variables.
557 if (Object.keys(c.b).length > Object.keys(c.a).length) {
558 const aux = c.a;
559 c.a=c.b;
560 c.b=aux;
561 }
562
563 // Mov to C if possible.
564 if (isConstant(c.a)) {
565 const ct = {t: "N", v: c.a.coefs[sONE]};
566 c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
567 c.a = { t: "LC", coefs: {} };
568 c.b = { t: "LC", coefs: {} };
569 }
570 if (isConstant(c.b)) {
571 const ct = {t: "N", v: c.b.coefs[sONE]};
572 c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
573 c.a = { t: "LC", coefs: {} };
574 c.b = { t: "LC", coefs: {} };
575 }
576
577 if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
578 const isolatedSignal = getFirstInternalSignal(ctx, c.c);
579 if (isolatedSignal) {
580
581 let lSignal = ctx.signals[isolatedSignal];
582 while (lSignal.e>=0) {
583 lSignal = ctx.signals[lSignal.e];
584 }
585
586
587 const isolatedSignalEquivalence = {
588 t: "LC",
589 coefs: {}
590 };
591 const invCoef = ctx.F.inv(c.c.coefs[isolatedSignal]);
592 for (const s in c.c.coefs) {
593 if (s != isolatedSignal) {
594 const v = ctx.F.mul( ctx.F.neg(c.c.coefs[s]), invCoef);
595 if (!ctx.F.isZero(v)) {
596 isolatedSignalEquivalence.coefs[s] = v;
597 }
598 }
599 }
600
601 for (let j in lSignal.inConstraints) {
602 if ((j!=i)&&(ctx.constraints[j])) {
603 ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
604 linkSignalsConstraint(j);
605 if (j<i) {
606 nextPossibleConstraints.push(j);
607 }
608 }
609 }
610
611 ctx.constraints[i] = null;
612
613 lSignal.c = ctx.stDISCARDED;
614 } else {
615 if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
616 }
617 }
618 }
619 possibleConstraints = nextPossibleConstraints;
620 }
621 unindexVariables();
622
623 // Pack the constraints
624 let o = 0;
625 for (let i=0; i<ctx.constraints.length; i++) {
626 if (ctx.constraints[i]) {
627 if (o != i) {
628 ctx.constraints[o] = ctx.constraints[i];
629 }
630 o++;
631 }
632 }
633 ctx.constraints.length = o;
634
635 function indexVariables() {
636 for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
637 }
638
639 function linkSignalsConstraint(cidx) {
640 const ct = ctx.constraints[cidx];
641 for (let k in ct.a.coefs) linkSignal(k, cidx);
642 for (let k in ct.b.coefs) linkSignal(k, cidx);
643 for (let k in ct.c.coefs) linkSignal(k, cidx);
644 }
645
646 function unindexVariables() {
647 for (let s=0; s<ctx.signals.length; s++) {
648 let lSignal = ctx.signals[s];
649 while (lSignal.e>=0) {
650 lSignal = ctx.signals[lSignal.e];
651 }
652 if (lSignal.inConstraints) delete lSignal.inConstraints;
653 }
654 }
655
656/*
657 function unlinkSignal(signalName, cidx) {
658 let lSignal = ctx.signals[signalName];
659 while (lSignal.e>=0) {
660 lSignal = ctx.signals[lSignal.e];
661 }
662 if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
663 delete lSignal.inConstraints[cidx];
664 }
665 }
666*/
667
668 function linkSignal(signalName, cidx) {
669 let lSignal = ctx.signals[signalName];
670 while (lSignal.e>=0) {
671 lSignal = ctx.signals[lSignal.e];
672 }
673 if (!lSignal.inConstraints) lSignal.inConstraints = {};
674 lSignal.inConstraints[cidx] = true;
675 }
676
677 function getFirstInternalSignal(ctx, l) {
678 for (let k in l.coefs) {
679 const signal = ctx.signals[k];
680 if (signal.c == ctx.stINTERNAL) return k;
681 }
682 return null;
683 }
684
685 function isConstant(l) {
686 for (let k in l.coefs) {
687 if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
688 }
689 if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
690 return true;
691 }
692
693}
694
695/*
696
697function buildCircuitDef(ctx, mainCode) {
698 const res = {
699 mainCode: mainCode
700 };
701 res.signalName2Idx = ctx.signalName2Idx;
702
703 res.components = [];
704 res.componentName2Idx = {};
705 for (let c in ctx.components) {
706 const idCoponent = res.components.length;
707 res.components.push({
708 name: c,
709 params: ctx.components[c].params,
710 template: ctx.components[c].template,
711 inputSignals: 0
712 });
713 res.componentName2Idx[c] = idCoponent;
714 }
715
716 res.signals = new Array(ctx.signalNames.length);
717 for (let i=0; i<ctx.signalNames.length; i++) {
718 res.signals[i] = {
719 names: ctx.signalNames[i],
720 triggerComponents: []
721 };
722 ctx.signalNames[i].map( (fullName) => {
723 const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
724 if (ctx.signals[fullName].direction == "IN") {
725 res.signals[i].triggerComponents.push(idComponet);
726 res.components[idComponet].inputSignals++;
727 }
728 });
729 }
730
731 res.constraints = buildConstraints(ctx);
732
733 res.templates = ctx.templates;
734
735 res.functions = {};
736 for (let f in ctx.functions) {
737 res.functions[f] = {
738 params: ctx.functionParams[f],
739 func: ctx.functions[f]
740 };
741 }
742
743 res.nPrvInputs = ctx.totals.prvInput;
744 res.nPubInputs = ctx.totals.pubInput;
745 res.nInputs = res.nPrvInputs + res.nPubInputs;
746 res.nOutputs = ctx.totals.output;
747 res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
748 res.nConstants = ctx.totals.constant;
749 res.nSignals = res.nVars + res.nConstants;
750
751 return res;
752}
753
754*/
755
756
757/*
758 Build constraints
759
760A constraint like this
761
762 [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0 ] = 0
763 [ 5*s2 + 6*s3] * [ s2 + ] - [s0 + 2* s2] = 0
764 [s1 + s3] * [ s2 + 5*s3] - [s4 ] = 0
765
766is converted to
767
768[
769 [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1" }],
770 [{ "2":"5","3":"6"} , {"2":"1" } , {"0":"1", "2":"2"}],
771 [{"1":"1", "3":"1"} , {"2":"1","3":"5"} , {"4":"1" }]
772]
773 ^ ^ ^
774 | | |
775 A B C
776
777*/
778/*
779function buildConstraints(ctx) {
780 const res = [];
781
782 function fillLC(dst, src) {
783 if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
784 for (let s in src.coefs) {
785 const v = src.coefs[s].toString();
786 const id = ctx.signalName2Idx[s];
787 dst[id] = v;
788 }
789 }
790
791 for (let i=0; i<ctx.constraints.length; i++) {
792 const A = {};
793 const B = {};
794 const C = {};
795
796 fillLC(A, ctx.constraints[i].a);
797 fillLC(B, ctx.constraints[i].b);
798 fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
799
800 res.push([A,B,C]);
801 }
802
803 return res;
804}
805*/
806
807/*
808function buildSyms(ctx, strm) {
809
810 let nSyms;
811
812 addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
813
814
815 function addSymbolsComponent(prefix, idComponet) {
816 for (let n in ctx.components[idComponet].names.o) {
817 const entrie = ctx.components[idComponet].names.o[n];
818 addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
819 }
820 }
821
822 function addSymbolArray(prefix, type, sizes, offset) {
823 if (sizes.length==0) {
824 if (type == "S") {
825 let s=offset;
826 while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
827 let wId = ctx.signals[s].id;
828 if (typeof(wId) == "undefined") wId=-1;
829 strm.write(`${offset},${wId},${prefix}\n`);
830 nSyms ++;
831 if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
832 } else {
833 addSymbolsComponent(prefix+".", offset);
834 }
835 return 1;
836 } else {
837 let acc = 0;
838 for (let i=0; i<sizes[0]; i++) {
839 acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
840 }
841 return acc;
842 }
843 }
844
845}
846
847*/
848