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