circom/ports/wasm/witness_calculator.js
ports/wasm/witness_calculator.jsRaw
1/* globals WebAssembly */
2
3const fs = require("fs");
4const utils = require("../../src/utils");
5const bigInt = require("big-integer");
6
7module.exports.fromFile = async function(file) {
8
9
10 const code = await fs.promises.readFile(file);
11
12 return await module.exports.fromBuffer(code);
13};
14
15module.exports.fromBuffer = async function(code) {
16
17 const memory = new WebAssembly.Memory({initial:20000});
18 const wasmModule = await WebAssembly.compile(code);
19
20 const instance = await WebAssembly.instantiate(wasmModule, {
21 env: {
22 "memory": memory
23 },
24 runtime: {
25 err: function(code, pstr) {
26 console.log("ERROR", code, p2str(pstr));
27 },
28 err1: function(code, pstr, a) {
29 console.log("ERROR: ", code, p2str(pstr), a);
30 },
31 err2: function(code, pstr, a, b) {
32 console.log("ERROR: ", code, p2str(pstr), a, b);
33 },
34 err3: function(code, pstr, a, b, c) {
35 console.log("ERROR: ", code, p2str(pstr), a, b, c);
36 },
37 err4: function(code, pstr, a,b,c,d) {
38 console.log("ERROR: ", code, p2str(pstr), a, b, c, d);
39 },
40 }
41 });
42
43 return new WitnessCalculator(memory, instance);
44
45 function p2str(p) {
46 return "TODO"+p;
47 }
48};
49
50class WitnessCalculator {
51 constructor(memory, instance) {
52 this.memory = memory;
53 this.i32 = new Uint32Array(memory.buffer);
54 this.instance = instance;
55
56 this.n32 = (this.instance.exports.getFrLen() >> 2) - 2;
57 const pRawPrime = this.instance.exports.getPRawPrime();
58
59 this.prime = bigInt(0);
60 for (let i=this.n32-1; i>=0; i--) {
61 this.prime = this.prime.shiftLeft(32);
62 this.prime = this.prime.add(bigInt(this.i32[(pRawPrime >> 2) + i]));
63 }
64
65 this.mask32 = bigInt("FFFFFFFF", 16);
66 this.NVars = this.instance.exports.getNVars();
67 this.n64 = Math.floor((this.prime.bitLength() - 1) / 64)+1;
68 this.R = bigInt.one.shiftLeft(this.n64*64);
69 this.RInv = this.R.modInv(this.prime);
70
71 }
72
73 async calculateWitness(input) {
74 const w = [];
75 const old0 = this.i32[0];
76 this.instance.exports.init();
77 const pSigOffset = this.allocInt();
78 const pFr = this.allocFr();
79 for (let k in input) {
80 const h = utils.fnvHash(k);
81 const hMSB = parseInt(h.slice(0,8), 16);
82 const hLSB = parseInt(h.slice(8,16), 16);
83 this.instance.exports.getSignalOffset32(pSigOffset, 0, hMSB, hLSB);
84 const sigOffset = this.getInt(pSigOffset);
85 const fArr = utils.flatArray(input[k]);
86 for (let i=0; i<fArr.length; i++) {
87 this.setFr(pFr, fArr[i]);
88 this.instance.exports.setSignal(0, 0, sigOffset + i, pFr);
89 }
90 }
91
92
93 for (let i=0; i<this.NVars; i++) {
94 const pWitness = this.instance.exports.getPWitness(i);
95 w.push(this.getFr(pWitness));
96 }
97
98 this.i32[0] = old0;
99 return w;
100 }
101
102 allocInt() {
103 const p = this.i32[0];
104 this.i32[0] = p+8;
105 return p;
106 }
107
108 allocFr() {
109 const p = this.i32[0];
110 this.i32[0] = p+this.n32*4 + 8;
111 return p;
112 }
113
114 getInt(p) {
115 return this.i32[p>>2];
116 }
117
118 setInt(p, v) {
119 this.i32[p>>2] = v;
120 }
121
122 getFr(p) {
123 const idx = (p>>2);
124
125 if (this.i32[idx + 1] & 0x80000000) {
126 let res= bigInt(0);
127 for (let i=this.n32-1; i>=0; i--) {
128 res = res.shiftLeft(32);
129 res = res.add(bigInt(this.i32[idx+2+i]));
130 }
131 if (this.i32[idx + 1] & 0x40000000) {
132 return fromMontgomery(res);
133 } else {
134 return res;
135 }
136
137 } else {
138 if (this.i32[idx] & 0x80000000) {
139 return this.prime.add( bigInt(this.i32[idx]).minus(bigInt(0x100000000)) );
140 } else {
141 return bigInt(this.i32[idx]);
142 }
143 }
144
145 function fromMontgomery(n) {
146 return n.times(this.RInv).mod(this.prime);
147 }
148
149 }
150
151
152 setFr(p, v) {
153 const self = this;
154 v = bigInt(v);
155
156 if (v.lt(bigInt("80000000", 16)) ) {
157 return setShortPositive(v);
158 }
159 if (v.geq(self.prime.minus(bigInt("80000000", 16))) ) {
160 return setShortNegative(v);
161 }
162 return setLongNormal(v);
163
164 function setShortPositive(a) {
165 self.i32[(p >> 2)] = parseInt(a);
166 self.i32[(p >> 2) + 1] = 0;
167 }
168
169 function setShortNegative(a) {
170 const b = bigInt("80000000", 16 ).add(a.minus( self.prime.minus(bigInt("80000000", 16 ))));
171 self.i32[(p >> 2)] = parseInt(b);
172 self.i32[(p >> 2) + 1] = 0;
173 }
174
175 function setLongNormal(a) {
176 self.i32[(p >> 2)] = 0;
177 self.i32[(p >> 2) + 1] = 0x80000000;
178 for (let i=0; i<self.n32; i++) {
179 self.i32[(p >> 2) + 2 + i] = a.shiftRight(i*32).and(self.mask32);
180 }
181 }
182 }
183}
184
185
186