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