circom/ports/wasm/builder.js
ports/wasm/builder.jsRaw
1const assert = require("assert");
2const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
3const ModuleBuilderWat = require("wasmbuilder").ModuleBuilderWat;
4const buildRuntime = require("./build_runtime");
5const Scalar = require("ffjavascript").Scalar;
6const F1Field = require("ffjavascript").F1Field;
7
8
9const errs = require("./errs");
10
11function hexToBytesR(hex) {
12 for (var bytes = [], c = hex.length-2; c >=0; c -= 2)
13 bytes.push(parseInt(hex.substr(c, 2), 16));
14 return bytes;
15}
16
17
18function intToBytes32(v) {
19 return [ v &0xFF, (v>>8) & 0xFF, (v>>16) & 0xFF, (v>>24) & 0xFF ];
20}
21
22
23class CodeBuilderWasm {
24 constructor(fnBuilder) {
25 this.fnBuilder = fnBuilder;
26 this.ops = [];
27 }
28
29 addComment(comment) {
30 this.ops.push({op: "COMMENT", comment});
31 }
32
33 addBlock(block) {
34 this.ops.push({op: "BLOCK", block});
35 }
36
37 calcOffset(dLabel, offsets) {
38 this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
39 }
40
41 assign(dLabel, src, sOffset) {
42 this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
43 }
44
45 getSubComponentOffset(dLabel, component, hash, hashLabel) {
46 this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
47 }
48
49 getSubComponentSizes(dLabel, component, hash, hashLabel) {
50 this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
51 }
52
53 getSignalOffset(dLabel, component, hash, hashLabel) {
54 this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
55 }
56
57 getSignalSizes(dLabel, component, hash, hashLabel) {
58 this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
59 }
60
61 setSignal(component, signal, value) {
62 this.ops.push({op: "SETSIGNAL", component, signal, value});
63 }
64
65 getSignal(dLabel, component, signal) {
66 this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
67 }
68
69 copyN(dLabel, offset, src, n) {
70 this.ops.push({op: "COPYN", dLabel, offset, src, n});
71 }
72
73 copyNRet(src, n) {
74 this.ops.push({op: "COPYNRET", src, n});
75 }
76
77 fieldOp(dLabel, fOp, params) {
78 this.ops.push({op: "FOP", dLabel, fOp, params});
79 }
80
81 ret() {
82 this.ops.push({op: "RET"});
83 }
84
85 addLoop(condLabel, body) {
86 this.ops.push({op: "LOOP", condLabel, body});
87 }
88
89 addIf(condLabel, thenCode, elseCode) {
90 this.ops.push({op: "IF", condLabel, thenCode, elseCode});
91 }
92
93 fnCall(fnName, retLabel, params) {
94 this.ops.push({op: "FNCALL", fnName, retLabel, params});
95 }
96
97 checkConstraint(a, b, strErr) {
98 this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
99 }
100
101
102 concat(cb) {
103 this.ops.push(...cb.ops);
104 }
105
106 log(val) {
107 this.ops.push({op: "LOG", val});
108 }
109
110 hasCode() {
111 for (let i=0; i<this.ops.length; i++) {
112 if (this.ops[i].op != "COMMENT") return true;
113 }
114 return false;
115 }
116
117
118 _buildOffset(c, offsets) {
119 let rN=0;
120 let S = null;
121 offsets.forEach((o) => {
122 if ((o[0][0] == "V") && (o[1][0]== "V")) {
123 rN += o[0][1]*o[1][1];
124 return;
125 }
126 let f=[];
127 if (o[0][0] == "V") {
128 f = c.i32_const(o[0][1]);
129 } else if (o[0][0] == "RI") {
130 f = c.i32_load(this.fnBuilder._getPtr(c, o[0][1]));
131 } else if (o[0][0] == "R") {
132 f = c.call("Fr_toInt", this.fnBuilder._getPtr(c, o[0][1]));
133 } else {
134 assert(false);
135 }
136 if (o[1][0] == "V") {
137 if (o[1][1]==0) return;
138 if (o[1][1]>1) {
139 f = c.i32_mul(f, c.i32_const(o[1][1]));
140 }
141 } else if (o[1][0] == "RS") {
142 f = c.i32_mul(
143 f,
144 c.i32_load(
145 c.i32_add(
146 c.i32_load(this.fnBuilder._getPtr(c, o[1][1])),
147 c.i32_const(o[1][2]*4)
148 )
149 )
150 );
151 } else {
152 assert(false);
153 }
154 if (S!=null) {
155 S = c.i32_add( S, f);
156 } else {
157 S = f;
158 }
159 });
160 if (rN>0) {
161 if (S!=null) {
162 S = c.i32_add( S, c.i32_const(rN));
163 } else {
164 S = c.i32_const(rN);
165 }
166 }
167 return S;
168 }
169
170 build(c) {
171 const code = [];
172 this.ops.forEach( (o) => {
173 if (o.op == "COMMENT") {
174 code.push(
175 c.comment(o.comment)
176 );
177 // DO nothing
178 } else if (o.op == "BLOCK") {
179 code.push(
180 o.block.build(c)
181 );
182 } else if (o.op == "CALCOFFSETS") {
183 code.push(
184 c.i32_store(
185 this.fnBuilder._getPtr(c, o.dLabel),
186 this._buildOffset(c, o.offsets)
187 )
188 );
189 } else if (o.op == "ASSIGN") {
190 code.push(
191 c.i32_store(
192 this.fnBuilder._getPtr(c, o.dLabel),
193 c.i32_add(
194 this.fnBuilder._deRefFr(c, o.src),
195 c.i32_mul(
196 this.fnBuilder._deRefInt(c, o.sOffset),
197 c.i32_const(this.fnBuilder.builder.sizeFr)
198 )
199 )
200 )
201 );
202 } else if (o.op == "GETSUBCOMPONENTOFFSET") {
203 code.push(
204 c.call(
205 "getSubComponentOffset",
206 this.fnBuilder._getPtr(c, o.dLabel),
207 this.fnBuilder._deRefInt(c, o.component),
208 c.i64_const("0x" + o.hash)
209 )
210 );
211 } else if (o.op == "GETSUBCOMPONENTSIZES") {
212 code.push(
213 c.call(
214 "getSubComponentSizes",
215 this.fnBuilder._getPtr(c, o.dLabel),
216 this.fnBuilder._deRefInt(c, o.component),
217 c.i64_const("0x" + o.hash)
218 )
219 );
220 } else if (o.op == "GETSIGNALOFFSET") {
221 code.push(
222 c.call(
223 "getSignalOffset",
224 this.fnBuilder._getPtr(c, o.dLabel),
225 this.fnBuilder._deRefInt(c, o.component),
226 c.i64_const("0x" + o.hash)
227 )
228 );
229 } else if (o.op == "GETSIGNALSIZES") {
230 code.push(
231 c.call(
232 "getSignalSizes",
233 this.fnBuilder._getPtr(c, o.dLabel),
234 this.fnBuilder._deRefInt(c, o.component),
235 c.i64_const("0x" + o.hash)
236 )
237 );
238 } else if (o.op == "SETSIGNAL") {
239 code.push(
240 c.call(
241 "setSignal",
242 c.getLocal("cIdx"),
243 this.fnBuilder._deRefInt(c, o.component),
244 this.fnBuilder._deRefInt(c, o.signal),
245 this.fnBuilder._deRefFr(c, o.value)
246 )
247 );
248 } else if (o.op == "GETSIGNAL") {
249 code.push(
250 c.call(
251 "getSignal",
252 c.getLocal("cIdx"),
253 this.fnBuilder._getPtr(c, o.dLabel),
254 this.fnBuilder._deRefInt(c, o.component),
255 this.fnBuilder._deRefInt(c, o.signal)
256 )
257 );
258 } else if (o.op == "COPYN") {
259 code.push(
260 c.call(
261 "Fr_copyn",
262 c.i32_add(
263 this.fnBuilder._getPtr(c, o.dLabel),
264 c.i32_mul(
265 this.fnBuilder._deRefInt(c, o.offset),
266 c.i32_const(this.fnBuilder.builder.sizeFr)
267 )
268 ),
269 this.fnBuilder._deRefFr(c, o.src),
270 c.i32_const(o.n)
271 )
272 );
273 } else if (o.op == "COPYNRET") {
274 code.push(
275 c.call(
276 "Fr_copyn",
277 c.getLocal("pRet"),
278 this.fnBuilder._deRefFr(c, o.src),
279 c.i32_const(o.n)
280 )
281 );
282 } else if (o.op == "RET") {
283 code.push(this.fnBuilder._freeStack(c));
284 code.push(c.ret([]));
285 } else if (o.op == "FOP") {
286 let params = [];
287 for (let i=0; i<o.params.length; i++) {
288 params.push( this.fnBuilder._deRefFr(c, o.params[i]));
289 }
290 code.push(
291 c.call(
292 "Fr_" + o.fOp,
293 this.fnBuilder._getPtr(c, o.dLabel),
294 ...params
295 )
296 );
297 } else if (o.op == "LOOP") {
298 code.push(
299 c.block(c.loop(
300 c.br_if(1, c.i32_eqz(c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])))),
301 o.body.build(c),
302 c.br(0)
303 ))
304 );
305 } else if (o.op == "IF") {
306 code.push(
307 c.if(
308 c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])),
309 o.thenCode.build(c),
310 o.elseCode ? o.elseCode.build(c) : undefined
311 )
312 );
313 } else if (o.op == "FNCALL") {
314 const params = [];
315 for (let i=0; i<o.params.length; i++) {
316 params.push(this.fnBuilder._deRefFr(c, ["R", o.params[i]]));
317 }
318 code.push(
319 c.call(
320 o.fnName,
321 this.fnBuilder._getPtr(c, o.retLabel),
322 ...params
323 )
324 );
325 } else if (o.op == "CHECKCONSTRAINT") {
326 code.push(
327 c.call(
328 "checkConstraint",
329 c.getLocal("cIdx"),
330 this.fnBuilder._deRefFr(c, o.a),
331 this.fnBuilder._deRefFr(c, o.b),
332 c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
333 )
334 );
335 } else if (o.op == "LOG") {
336 code.push(
337 c.call(
338 "log",
339 this.fnBuilder._deRefFr(c, o.val)
340 )
341 );
342 }
343 });
344 return code;
345 }
346}
347
348class FunctionBuilderWasm {
349
350 constructor(builder, name, instanceDef, type, params) {
351 this.builder = builder;
352 this.name = name;
353 this.instanceDef = instanceDef;
354 this.type = type; // "COMPONENT" or "FUNCTION"
355 this.definedFrElements = [];
356 this.definedIntElements = [];
357 this.definedSizeElements = [];
358 this.definedPFrElements = [];
359 this.initializedElements = [];
360 this.initializedSignalOffset = [];
361 this.initializedSignalSizes = [];
362
363 this.refs = {};
364
365 this.nFr = 0;
366 this.nInt = 0;
367 this.nSizes = 0;
368 this.nPFr = 0;
369
370 }
371
372 defineFrElements(dLabel, size) {
373 this.refs[dLabel] = {
374 t: "Fr",
375 n: size,
376 idx: this.nFr
377 };
378 this.nFr += size;
379 }
380
381 defineIntElement(dLabel) {
382 this.refs[dLabel] = {
383 t: "I",
384 idx: this.nInt
385 };
386 this.nInt++;
387 }
388
389 defineSizesElement(dLabel) {
390 this.refs[dLabel] = {
391 t: "S",
392 idx: this.nSizes
393 };
394 this.nSizes++;
395 }
396
397 definePFrElement(dLabel) {
398 this.refs[dLabel] = {
399 t: "PFr",
400 idx: this.nPFr
401 };
402 this.nPFr++;
403 }
404
405 initializeFrElement(dLabel, offset, idConstant) {
406 this.initializedElements.push({dLabel, offset, idConstant});
407 }
408
409 initializeSignalOffset(dLabel, component, hash, hashLabel) {
410 this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
411 }
412
413 initializeSignalSizes(dLabel, component, hash, hashLabel) {
414 this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
415 }
416
417 setParams(params) {
418 assert(typeof this.params === "undefined");
419 assert(this.type === "FUNCTION");
420 this.params=params;
421 for (let i=0; i<this.params.length; i++) {
422 assert(typeof this.refs[this.params[i]] === "undefined");
423 this.refs[this.params[i]] = {
424 t: "P",
425 l: this.params[i]
426 };
427 }
428 }
429
430 _getPtr(c, label, offset) {
431 offset = offset || 0;
432 assert (typeof this.refs[label] !== "undefined");
433 if (this.refs[label].t == "Fr") {
434 return c.i32_add(
435 c.getLocal("sp"),
436 c.i32_const((this.refs[label].idx + offset) * this.builder.sizeFr)
437 );
438 } else if (this.refs[label].t == "I") {
439 assert(offset == 0);
440 return c.i32_add(
441 c.getLocal("sp"),
442 c.i32_const(
443 this.nFr * this.builder.sizeFr +
444 this.refs[label].idx * 4)
445 );
446 } else if (this.refs[label].t == "S") {
447 assert(offset == 0);
448 return c.i32_add(
449 c.getLocal("sp"),
450 c.i32_const(
451 this.nFr * this.builder.sizeFr +
452 this.nInt * 4 +
453 this.refs[label].idx * 4)
454 );
455 } else if (this.refs[label].t == "PFr") {
456 assert(offset == 0);
457 return c.i32_add(
458 c.getLocal("sp"),
459 c.i32_const(
460 this.nFr * this.builder.sizeFr +
461 this.nInt * 4 +
462 this.nSizes * 4 +
463 this.refs[label].idx * 4)
464 );
465 } else if (this.refs[label].t == "P") {
466 return c.i32_add(
467 c.getLocal(this.refs[label].l),
468 c.i32_const( offset * this.builder.sizeFr )
469 );
470 }
471 }
472
473 _getPtrConstant(c, idConstant) {
474 return c.i32_const( this.builder.pConstants + idConstant * this.builder.sizeFr );
475 }
476
477 _deRefInt(c, ref) {
478 if (ref[0] == "R") {
479 return c.call(
480 "Fr_toInt",
481 this._getPtr(c, ref[1])
482 );
483 } else if (ref[0] == "RI") {
484 return c.i32_load(this._getPtr(c, ref[1]));
485 } else if (ref[0] == "V") {
486 return c.i32_const(ref[1]);
487 } else if (ref[0] == "C") {
488 return c.call(
489 "Fr_toInt",
490 this._getPtrConstant(c, ref[1])
491 );
492 } else if (ref[0] == "CC") {
493 return c.getLocal("cIdx");
494 } else {
495 assert(false);
496 }
497 }
498
499 _deRefFr(c, ref) {
500 if (ref[0] == "R") {
501 if (this.refs[ref[1]].t == "Fr") {
502 return this._getPtr(c, ref[1]);
503 } else if (this.refs[ref[1]].t == "PFr") {
504 return c.i32_load(this._getPtr(c, ref[1]));
505 } else if (this.refs[ref[1]].t == "P") {
506 return c.getLocal(ref[1]);
507 } else {
508 assert(false);
509 }
510 } else if (ref[0] == "C") {
511 return this._getPtrConstant(c, ref[1]);
512 } else {
513 assert(false);
514 }
515 }
516
517 _reserveStack(c) {
518 const code = [];
519 code.push(
520 // Load SP
521 c.setLocal("sp", c.i32_load(c.i32_const(4))),
522
523 // Check we have enough memory
524 c.if(
525 c.i32_gt_u(
526 c.i32_const(this.nStackSize),
527 c.getLocal("sp")
528 ),
529 c.call(
530 "error",
531 c.i32_const(errs.STACK_OUT_OF_MEM.code),
532 c.i32_const(errs.STACK_OUT_OF_MEM.pointer),
533 c.i32_const(0),
534 c.i32_const(0),
535 c.i32_const(0),
536 c.i32_const(0)
537 )
538 ),
539
540 // Reserve space in sp
541 c.setLocal(
542 "sp",
543 c.i32_sub(
544 c.getLocal("sp"),
545 c.i32_const(this.nStackSize)
546 )
547 ),
548
549 // Check if we have enought free memory
550 c.if(
551 c.i32_gt_u(
552 c.i32_load(c.i32_const(0)),
553 c.getLocal("sp")
554 ),
555 c.call(
556 "error",
557 c.i32_const(errs.STACK_TOO_SMALL.code),
558 c.i32_const(errs.STACK_TOO_SMALL.pointer),
559 c.i32_const(0),
560 c.i32_const(0),
561 c.i32_const(0),
562 c.i32_const(0)
563 )
564 ),
565
566 // Save sp
567 c.i32_store(c.i32_const(4), c.getLocal("sp"))
568 );
569 return code;
570 }
571
572 _freeStack(c) {
573 const code = [];
574 code.push(
575 c.i32_store(
576 c.i32_const(4),
577 c.i32_add(
578 c.getLocal("sp"),
579 c.i32_const(this.nStackSize)
580 )
581 )
582 );
583 return code;
584 }
585
586 _buildHeader(c) {
587 const code = [];
588 this.nStackSize =
589 this.nFr * this.builder.sizeFr +
590 this.nInt * 4 +
591 this.nSizes * 4 +
592 this.nPFr * 4;
593
594
595 code.push( this._reserveStack(c));
596
597 this.initializedElements.forEach( (o) => {
598 code.push(
599 c.call(
600 "Fr_copy",
601 this._getPtr(c, o.dLabel, o.offset),
602 this._getPtrConstant(c, o.idConstant)
603 )
604 );
605 });
606 this.initializedSignalOffset.forEach( (o) => {
607 code.push(
608 c.call(
609 "getSignalOffset",
610 this._getPtr(c, o.dLabel, o.offset),
611 this._deRefInt(c, o.component),
612 c.i64_const("0x" + o.hash)
613 )
614 );
615 });
616 this.initializedSignalSizes.forEach( (o) => {
617 code.push(
618 c.call(
619 "getSignalSizes",
620 this._getPtr(c, o.dLabel, o.offset),
621 this._deRefInt(c, o.component),
622 c.i64_const("0x" + o.hash)
623 )
624 );
625 });
626 return code;
627 }
628
629 _buildFooter(c) {
630 return this._freeStack(c);
631 }
632
633 newCodeBuilder() {
634 return new CodeBuilderWasm(this);
635 }
636
637 setBody(body) {
638 this.body = body;
639 }
640
641 build(module) {
642
643
644 const f = module.addFunction(this.name, this.instanceDef);
645
646 if (this.type=="COMPONENT") {
647 f.addParam("cIdx", "i32");
648 } else if (this.type=="FUNCTION") {
649 f.addParam("pRet", "i32");
650 for (let i=0;i<this.params.length;i++ ) {
651 f.addParam(this.params[i], "i32");
652 }
653 } else {
654 assert(false);
655 }
656 f.addLocal("sp", "i32");
657
658 const c = f.getCodeBuilder();
659
660
661 const code = [];
662 if (this.type=="COMPONENT") {
663 code.push(c.call("componentStarted", c.getLocal("cIdx")));
664 }
665
666 code.push(this._buildHeader(c));
667 code.push(this.body.build(c));
668 if (this.type=="COMPONENT") {
669 code.push(c.call("componentFinished", c.getLocal("cIdx")));
670 }
671 code.push(this._buildFooter(c));
672
673 f.addCode(flatArray(code));
674
675 function flatArray(c) {
676 const res=[];
677 for (let i=0; i<c.length; i++) {
678 if (Array.isArray(c[i])) {
679 res.push(...flatArray(c[i]));
680 } else {
681 res.push(c[i]);
682 }
683 }
684 return res;
685 }
686 }
687
688}
689
690class BuilderWasm {
691 constructor(p) {
692 this.F = new F1Field(p);
693 this.hashMaps={};
694 this.componentEntriesTables={};
695 this.sizes ={};
696 this.constants = [];
697 this.usedConstants = {};
698 this.functions = [];
699 this.components = [];
700
701 this.TYPE_SIGNAL = 1;
702 this.TYPE_COMPONENT = 2;
703
704 this.addConstant(Scalar.fromString("0")); // constants[0] = 0;
705 this.addConstant(Scalar.fromString("1")); // constants[1] = 1;
706
707 this.offsetComponentNInputSignals = 12;
708 this.sizeofComponent = 20;
709 }
710
711 setHeader(header) {
712 this.header=header;
713
714 this.n64 = Math.floor((Scalar.bitLength(this.header.P) - 1) / 64)+1;
715 this.sizeFr = this.n64*8 + 8;
716 }
717
718 // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
719 addHashMap(name, table) {
720 this.hashMaps[name] = {table};
721 }
722
723 addComponentEntriesTable(name, cet) {
724 this.componentEntriesTables[name] = cet;
725 }
726
727 addSizes(name, table) {
728 this.sizes[name] = {table};
729 }
730
731 addConstant(c) {
732 c = this.F.e(c);
733 const cS = c.toString();
734 if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
735 this.constants.push(c);
736 this.usedConstants[cS] = this.constants.length - 1;
737 return this.constants.length - 1;
738 }
739
740 addFunction(fnBuilder) {
741 this.functions.push(fnBuilder);
742 }
743
744 addComponent(component) {
745 this.components.push(component);
746 }
747
748 setMapIsInput(map) {
749 this.mapIsInput = map;
750 }
751
752 setWit2Sig(wit2sig) {
753 this.wit2sig = wit2sig;
754 }
755
756
757 newComponentFunctionBuilder(name, instanceDef) {
758 return new FunctionBuilderWasm(this, name, instanceDef, "COMPONENT");
759 }
760
761 newFunctionBuilder(name, instanceDef, params) {
762 return new FunctionBuilderWasm(this, name, instanceDef, "FUNCTION", params);
763 }
764
765
766 // Body functions
767
768 _buildHeader(module) {
769
770 this.pCircuit = module.alloc(48);
771
772 this.pNSignals = this.pCircuit;
773 this.pNComponents = this.pCircuit + 4;
774 this.pNInputs = this.pCircuit + 8;
775 this.pNOutputs = this.pCircuit + 12;
776 this.pNVars = this.pCircuit + 16;
777 this.ppWit2sig = this.pCircuit + 20;
778 this.ppComponents = this.pCircuit + 24;
779 this.ppMapIsInput = this.pCircuit + 28;
780 this.ppConstants = this.pCircuit + 32;
781 this.ppSignals = this.pCircuit + 36;
782 this.ppInputSignalsToTrigger = this.pCircuit + 40;
783 this.ppSignalsAssigned = this.pCircuit + 44;
784 }
785
786 _buildSizes(module) {
787 for (let sName in this.sizes) {
788 const accSizes = this.sizes[sName];
789
790 const bytes = [];
791
792 for (let i=0; i<accSizes.table.length; i++) {
793 bytes.push(intToBytes32(accSizes.table[i]));
794 }
795
796 const fBytes = [].concat(...bytes);
797 accSizes.pointer = module.alloc(fBytes);
798 }
799 }
800
801
802 _buildHashMaps(module) {
803
804 for (let hmName in this.hashMaps ) {
805 const hm = this.hashMaps[hmName];
806
807 const bytes = [];
808
809 for (let i=0; i<256; i++) {
810 if (hm.table[i]) {
811 bytes.push(hexToBytesR(hm.table[i][0]));
812 bytes.push(intToBytes32(hm.table[i][1]));
813 } else {
814 bytes.push([0,0,0,0,0,0,0,0,0,0,0,0]);
815 }
816 }
817
818 const fBytes = [].concat(...bytes);
819 hm.pointer = module.alloc(fBytes);
820 }
821
822 }
823
824 _buildComponentEntriesTables(module) {
825
826 for (let cetName in this.componentEntriesTables) {
827 const cet = this.componentEntriesTables[cetName];
828
829 const bytes = [];
830
831 for (let j=0; j<cet.length; j++) {
832 const ty = cet[j].type == "S" ? this.TYPE_SIGNAL : this.TYPE_COMPONENT;
833 bytes.push(intToBytes32(cet[j].offset));
834 bytes.push(intToBytes32(this.sizes[cet[j].sizeName].pointer));
835 bytes.push(intToBytes32(ty));
836 }
837
838 const fBytes = [].concat(...bytes);
839 this.componentEntriesTables[cetName].pointer = module.alloc(fBytes);
840 }
841 }
842
843
844 _buildConstants(module) {
845 const self = this;
846
847 const bytes = [];
848 for (let i=0; i<self.constants.length; i++) {
849 bytes.push(Fr2Bytes(self.constants[i]));
850 }
851
852 const fBytes = [].concat(...bytes);
853 this.pConstants = module.alloc(fBytes);
854
855
856 function Fr2Bytes(n) {
857 const minShort = self.F.neg(self.F.e("80000000"));
858 const maxShort = self.F.e("7FFFFFFF", 16);
859
860 if ( (self.F.geq(n, minShort))
861 &&(self.F.leq(n, maxShort)))
862 {
863 if (self.F.geq(n, self.F.zero)) {
864 return shortMontgomeryPositive(n);
865 } else {
866 return shortMontgomeryNegative(n);
867 }
868 }
869
870 return longMontgomery(n);
871
872
873 function shortMontgomeryPositive(a) {
874 return [
875 ...intToBytes32(Scalar.toNumber(a)),
876 ...intToBytes32(0x40000000),
877 ...long(toMontgomery(a))
878 ];
879 }
880
881
882 function shortMontgomeryNegative(a) {
883 const b = -Scalar.toNumber(self.F.neg(a));
884 return [
885 ...intToBytes32(b),
886 ...intToBytes32(0x40000000),
887 ...long(toMontgomery(a))
888 ];
889 }
890
891 function longMontgomery(a) {
892 return [
893 ...intToBytes32(0),
894 ...intToBytes32(0xC0000000),
895 ...long(toMontgomery(a))
896 ];
897 }
898
899 function long(a) {
900
901 const bytes = [];
902 const arr = Scalar.toArray(a, 0x100000000);
903 for (let i=0; i<self.F.n64*2; i++) {
904 const idx = arr.length-1-i;
905
906 if ( idx >=0) {
907 bytes.push(...intToBytes32(arr[idx]));
908 } else {
909 bytes.push(...intToBytes32(0));
910 }
911 }
912
913 return bytes;
914 }
915
916 function toMontgomery(a) {
917 return self.F.mul(a, self.F.R);
918 }
919
920 }
921 }
922
923 _buildFunctions(module) {
924 for (let i=0; i<this.functions.length; i++) {
925 const cfb = this.functions[i];
926 cfb.build(module);
927 }
928 }
929
930 _buildComponents(module) {
931 const bytes = new Array(this.components.length*5*4);
932 bytes.length=0;
933 for (let i=0; i<this.components.length; i++) {
934 const c = this.components[i];
935
936 bytes.push(...intToBytes32(this.hashMaps[c.hashMapName].pointer));
937 bytes.push(...intToBytes32(this.componentEntriesTables[c.entryTableName].pointer));
938 bytes.push(...intToBytes32(i));
939 bytes.push(...intToBytes32(c.nInSignals));
940 bytes.push(...intToBytes32(c.newThread ? 1 : 0));
941
942 module.addFunctionToTable(c.functionName);
943 }
944
945 this.pComponents = module.alloc(bytes);
946 }
947
948 _buildMapIsInput(module) {
949 const bytes = new Array(this.mapIsInput.length*4);
950 bytes.length=0;
951 for (let i=0; i<this.mapIsInput.length; i++) {
952 bytes.push(...intToBytes32(this.mapIsInput[i]));
953 }
954
955 this.pMapIsInput = module.alloc(bytes);
956 }
957
958 _buildWit2Sig(module) {
959 const bytes = new Array(this.wit2sig.length*4);
960 bytes.length =0;
961 for (let i=0; i<this.wit2sig.length; i++) {
962 bytes.push(...intToBytes32(this.wit2sig[i]));
963 }
964
965 this.pWit2sig = module.alloc(bytes);
966 }
967
968 _buildCircuitVar(module) {
969 module.addData(this.pNSignals, intToBytes32(this.header.NSignals));
970 module.addData(this.pNComponents, intToBytes32(this.header.NComponents));
971 module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
972 module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
973 module.addData(this.pNVars, intToBytes32(this.header.NVars));
974 module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
975 module.addData(this.ppComponents, intToBytes32(this.pComponents));
976 module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
977 module.addData(this.ppConstants, intToBytes32(this.pConstants));
978 module.addData(this.ppSignals, intToBytes32(this.pSignals));
979 module.addData(this.ppInputSignalsToTrigger, intToBytes32(this.pInputSignalsToTrigger));
980 module.addData(this.ppSignalsAssigned, intToBytes32(this.pSignalsAssigned));
981
982 }
983
984 _buildErrors(module) {
985 for (let e in errs) {
986 errs[e].pointer = module.allocString(errs[e].str);
987 }
988 }
989
990
991 async build(fd, outType) {
992 const encoder = new TextEncoder("utf-8");
993 let module;
994 if (outType == "wasm") {
995 module=new ModuleBuilder();
996 } else if (outType == "wat") {
997 module=new ModuleBuilderWat();
998 } else {
999 assert(false);
1000 }
1001 this.module = module;
1002
1003 // First of all reseve the space for the header so this has a fixed position starting at 8.
1004 this._buildHeader(module);
1005 this._buildErrors(module);
1006 buildRuntime(module, this);
1007 this._buildSizes(module);
1008 this._buildConstants(module);
1009 this._buildHashMaps(module);
1010 this._buildComponentEntriesTables(module);
1011 this._buildFunctions(module);
1012 this._buildComponents(module);
1013 this._buildMapIsInput(module);
1014 this._buildWit2Sig(module);
1015 this._buildCircuitVar(module);
1016
1017 module.setMemory(2000);
1018 if (outType == "wasm") {
1019 const bytes = module.build();
1020 const bytesArr = new Uint8Array(bytes);
1021 await fd.write(bytesArr);
1022 } else if (outType == "wat") {
1023 const code = module.build();
1024 await writeCode(code);
1025 } else {
1026 assert(false);
1027 }
1028
1029 async function writeCode(c) {
1030 if (c.push) {
1031 for (let i=0; i<c.length; i++) {
1032 await writeCode(c[i]);
1033 }
1034 } else if (typeof c === "string") {
1035 await fd.write(encoder.encode(c + "\n"));
1036 }
1037 }
1038 }
1039}
1040
1041
1042module.exports = BuilderWasm;