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