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 let wc;
21
22 const instance = await WebAssembly.instantiate(wasmModule, {
23 env: {
24 "memory": memory
25 },
26 runtime: {
27 err: function(code, pstr) {
28 const errStr=p2str(pstr);
29 console.log("ERROR: ", code, errStr);
30 throw new Error(errStr);
31 },
32 err1: function(code, pstr, a) {
33 const errStr=p2str(pstr)+ " " + a;
34 console.log("ERROR: ", code, errStr);
35 throw new Error(errStr);
36 },
37 err2: function(code, pstr, a, b) {
38 const errStr=p2str(pstr)+ " " + a + " " + b;
39 console.log("ERROR: ", code, errStr);
40 throw new Error(errStr);
41 },
42 err3: function(code, pstr, a, b, c) {
43 const errStr=p2str(pstr)+ " " + a + " " + b + " " + c;
44 console.log("ERROR: ", code, errStr);
45 throw new Error(errStr);
46 },
47 err4: function(code, pstr, a,b,c,d) {
48 const errStr=p2str(pstr) + " " + wc.getFr(b).toString() + " != " + wc.getFr(c).toString() + " " +p2str(d);
49 console.log("ERROR: ", code, errStr);
50 throw new Error(errStr);
51 },
52 log: function(a) {
53 console.log(wc.getFr(a).toString());
54 },
55 }
56 });
57
58 wc = new WitnessCalculator(memory, instance);
59 return wc;
60
61 function p2str(p) {
62 const i8 = new Uint8Array(memory.buffer);
63
64 const bytes = [];
65
66 for (let i=0; i8[p+i]>0; i++) bytes.push(i8[p+i]);
67
68 return String.fromCharCode.apply(null, bytes);
69 }
70};
71
72class WitnessCalculator {
73 constructor(memory, instance) {
74 this.memory = memory;
75 this.i32 = new Uint32Array(memory.buffer);
76 this.instance = instance;
77
78 this.n32 = (this.instance.exports.getFrLen() >> 2) - 2;
79 const pRawPrime = this.instance.exports.getPRawPrime();
80
81 this.prime = bigInt(0);
82 for (let i=this.n32-1; i>=0; i--) {
83 this.prime = this.prime.shiftLeft(32);
84 this.prime = this.prime.add(bigInt(this.i32[(pRawPrime >> 2) + i]));
85 }
86
87 this.mask32 = bigInt("FFFFFFFF", 16);
88 this.NVars = this.instance.exports.getNVars();
89 this.n64 = Math.floor((this.prime.bitLength() - 1) / 64)+1;
90 this.R = bigInt.one.shiftLeft(this.n64*64);
91 this.RInv = this.R.modInv(this.prime);
92
93 }
94
95 async calculateWitness(input) {
96 const w = [];
97 const old0 = this.i32[0];
98 this.instance.exports.init();
99 const pSigOffset = this.allocInt();
100 const pFr = this.allocFr();
101 for (let k in input) {
102 const h = utils.fnvHash(k);
103 const hMSB = parseInt(h.slice(0,8), 16);
104 const hLSB = parseInt(h.slice(8,16), 16);
105 this.instance.exports.getSignalOffset32(pSigOffset, 0, hMSB, hLSB);
106 const sigOffset = this.getInt(pSigOffset);
107 const fArr = utils.flatArray(input[k]);
108 for (let i=0; i<fArr.length; i++) {
109 this.setFr(pFr, fArr[i]);
110 this.instance.exports.setSignal(0, 0, sigOffset + i, pFr);
111 }
112 }
113
114
115 for (let i=0; i<this.NVars; i++) {
116 const pWitness = this.instance.exports.getPWitness(i);
117 w.push(this.getFr(pWitness));
118 }
119
120 this.i32[0] = old0;
121 return w;
122 }
123
124 allocInt() {
125 const p = this.i32[0];
126 this.i32[0] = p+8;
127 return p;
128 }
129
130 allocFr() {
131 const p = this.i32[0];
132 this.i32[0] = p+this.n32*4 + 8;
133 return p;
134 }
135
136 getInt(p) {
137 return this.i32[p>>2];
138 }
139
140 setInt(p, v) {
141 this.i32[p>>2] = v;
142 }
143
144 getFr(p) {
145 const self = this;
146 const idx = (p>>2);
147
148 if (self.i32[idx + 1] & 0x80000000) {
149 let res= bigInt(0);
150 for (let i=self.n32-1; i>=0; i--) {
151 res = res.shiftLeft(32);
152 res = res.add(bigInt(self.i32[idx+2+i]));
153 }
154 if (self.i32[idx + 1] & 0x40000000) {
155 return fromMontgomery(res);
156 } else {
157 return res;
158 }
159
160 } else {
161 if (self.i32[idx] & 0x80000000) {
162 return self.prime.add( bigInt(self.i32[idx]).minus(bigInt(0x100000000)) );
163 } else {
164 return bigInt(self.i32[idx]);
165 }
166 }
167
168 function fromMontgomery(n) {
169 return n.times(self.RInv).mod(self.prime);
170 }
171
172 }
173
174
175 setFr(p, v) {
176 const self = this;
177 v = bigInt(v);
178
179 if (v.lt(bigInt("80000000", 16)) ) {
180 return setShortPositive(v);
181 }
182 if (v.geq(self.prime.minus(bigInt("80000000", 16))) ) {
183 return setShortNegative(v);
184 }
185 return setLongNormal(v);
186
187 function setShortPositive(a) {
188 self.i32[(p >> 2)] = parseInt(a);
189 self.i32[(p >> 2) + 1] = 0;
190 }
191
192 function setShortNegative(a) {
193 const b = bigInt("80000000", 16 ).add(a.minus( self.prime.minus(bigInt("80000000", 16 ))));
194 self.i32[(p >> 2)] = parseInt(b);
195 self.i32[(p >> 2) + 1] = 0;
196 }
197
198 function setLongNormal(a) {
199 self.i32[(p >> 2)] = 0;
200 self.i32[(p >> 2) + 1] = 0x80000000;
201 for (let i=0; i<self.n32; i++) {
202 self.i32[(p >> 2) + 2 + i] = a.shiftRight(i*32).and(self.mask32);
203 }
204 }
205 }
206}
207
208
209