arnaucube
arnaucube/circomPublic Self-hosted repository
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