diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 88% rename from .eslintrc.js rename to .eslintrc.cjs index ce75c5c..a139e2b 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -5,7 +5,8 @@ module.exports = { "mocha": true }, "parserOptions": { - "ecmaVersion": 2017 + "ecmaVersion": 2022, + "sourceType": "module" }, "extends": "eslint:recommended", "rules": { diff --git a/build/main.cjs b/build/main.cjs index 1697598..948162a 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -23,13 +23,13 @@ limitations under the License. */ function flatArray(a) { - var res = []; + let res = []; fillArray(res, a); return res; function fillArray(res, a) { if (Array.isArray(a)) { - for (let i=0; i0) { - res.unshift(0); - i--; - } + let i = size - res.length; + while (i > 0) { + res.unshift(0); + i--; + } } return res; } @@ -84,7 +84,6 @@ async function builder(code, options) { let instance; let wc; let memory; - options = options || {}; // Only circom 2 implements version lookup through exports in the WASM @@ -93,27 +92,37 @@ async function builder(code, options) { let majorVersion = 1; // After Circom 2.0.7, Blaine added exported functions for getting minor and patch versions let minorVersion = 0; - // If we can't lookup the patch version, assume the lowest + // If we can't look up the patch version, assume the lowest let patchVersion = 0; let codeIsWebAssemblyInstance = false; + // If code is already prepared WebAssembly.Instance, we use it directly if (code instanceof WebAssembly.Instance) { instance = code; codeIsWebAssemblyInstance = true; } else { let memorySize = 32767; + + if (options.memorySize) { + // make sure we have int + memorySize = parseInt(options.memorySize); + if (memorySize < 0) { + throw new Error("Invalid memory size"); + } + } + let memoryAllocated = false; - while (!memoryAllocated){ - try{ - memory = new WebAssembly.Memory({initial:memorySize}); + while (!memoryAllocated) { + try { + memory = new WebAssembly.Memory({initial: memorySize}); memoryAllocated = true; - } catch(err){ - if(memorySize === 1){ + } catch (err) { + if (memorySize <= 1) { throw err; } console.warn("Could not allocate " + memorySize * 1024 * 64 + " bytes. This may cause severe instability. Trying with " + memorySize * 1024 * 64 / 2 + " bytes"); - memorySize = Math.floor(memorySize/2); + memorySize = Math.floor(memorySize / 2); } } @@ -127,19 +136,19 @@ async function builder(code, options) { "memory": memory }, runtime: { - exceptionHandler: function(code) { + exceptionHandler: function (code) { let err; - if (code == 1) { + if (code === 1) { err = "Signal not found. "; - } else if (code == 2) { + } else if (code === 2) { err = "Too many signals set. "; - } else if (code == 3) { + } else if (code === 3) { err = "Signal already set. "; - } else if (code == 4) { + } else if (code === 4) { err = "Assert Failed. "; - } else if (code == 5) { + } else if (code === 5) { err = "Not enough memory. "; - } else if (code == 6) { + } else if (code === 6) { err = "Input signal array access exceeds the size. "; } else { err = "Unknown error. "; @@ -149,10 +158,10 @@ async function builder(code, options) { }, // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports // `printErrorMessage` and `writeBufferMessage`. - printErrorMessage: function() { + printErrorMessage: function () { errStr += getMessage() + "\n"; }, - writeBufferMessage: function() { + writeBufferMessage: function () { const msg = getMessage(); // Any calls to `log()` will always end with a `\n`, so that's when we print and reset if (msg === "\n") { @@ -167,11 +176,11 @@ async function builder(code, options) { msgStr += msg; } }, - showSharedRWMemory: function() { + showSharedRWMemory: function () { const shared_rw_memory_size = instance.exports.getFieldNumLen32(); const arr = new Uint32Array(shared_rw_memory_size); - for (let j=0; j0; i++) bytes.push(i8[p+i]); + for (let i = 0; i8[p + i] > 0; i++) bytes.push(i8[p + i]); return String.fromCharCode.apply(null, bytes); } } + class WitnessCalculatorCircom1 { constructor(memory, instance, sanityCheck) { this.memory = memory; @@ -292,8 +302,8 @@ class WitnessCalculatorCircom1 { const pRawPrime = this.instance.exports.getPRawPrime(); const arr = new Array(this.n32); - for (let i=0; i> 2) + i]; + for (let i = 0; i < this.n32; i++) { + arr[this.n32 - 1 - i] = this.i32[(pRawPrime >> 2) + i]; } this.prime = ffjavascript.Scalar.fromArray(arr, 0x100000000); @@ -302,8 +312,8 @@ class WitnessCalculatorCircom1 { this.mask32 = ffjavascript.Scalar.fromString("FFFFFFFF", 16); this.NVars = this.instance.exports.getNVars(); - this.n64 = Math.floor((this.Fr.bitLength - 1) / 64)+1; - this.R = this.Fr.e( ffjavascript.Scalar.shiftLeft(1 , this.n64*64)); + this.n64 = Math.floor((this.Fr.bitLength - 1) / 64) + 1; + this.R = this.Fr.e(ffjavascript.Scalar.shiftLeft(1, this.n64 * 64)); this.RInv = this.Fr.inv(this.R); this.sanityCheck = sanityCheck; } @@ -317,10 +327,10 @@ class WitnessCalculatorCircom1 { const pSigOffset = this.allocInt(); const pFr = this.allocFr(); const keys = Object.keys(input); - keys.forEach( (k) => { + keys.forEach((k) => { const h = fnvHash(k); - const hMSB = parseInt(h.slice(0,8), 16); - const hLSB = parseInt(h.slice(8,16), 16); + const hMSB = parseInt(h.slice(0, 8), 16); + const hLSB = parseInt(h.slice(8, 16), 16); try { this.instance.exports.getSignalOffset32(pSigOffset, 0, hMSB, hLSB); } catch (err) { @@ -328,7 +338,7 @@ class WitnessCalculatorCircom1 { } const sigOffset = this.getInt(pSigOffset); const fArr = flatArray(input[k]); - for (let i=0; i>2]; + return this.i32[p >> 2]; } setInt(p, v) { - this.i32[p>>2] = v; + this.i32[p >> 2] = v; } getFr(p) { const self = this; - const idx = (p>>2); + const idx = (p >> 2); if (self.i32[idx + 1] & 0x80000000) { const arr = new Array(self.n32); - for (let i=0; i> 2)] = 0; self.i32[(p >> 2) + 1] = 0x80000000; const arr = ffjavascript.Scalar.toArray(v, 0x100000000); - for (let i=0; i=0) { + if (idx >= 0) { self.i32[(p >> 2) + 2 + i] = arr[idx]; } else { self.i32[(p >> 2) + 2 + i] = 0; @@ -466,8 +475,8 @@ class WitnessCalculatorCircom2 { this.instance.exports.getRawPrime(); const arr = new Uint32Array(this.n32); - for (let i=0; i { + let input_counter = 0; + keys.forEach((k) => { const h = fnvHash(k); - const hMSB = parseInt(h.slice(0,8), 16); - const hLSB = parseInt(h.slice(8,16), 16); + const hMSB = parseInt(h.slice(0, 8), 16); + const hLSB = parseInt(h.slice(8, 16), 16); const fArr = flatArray(input[k]); // Slight deviation from https://github.com/iden3/circom/blob/v2.1.6/code_producers/src/wasm_elements/common/witness_calculator.js // because I don't know when this exported function was added - if (typeof this.instance.exports.getInputSignalSize === 'function') { + if (typeof this.instance.exports.getInputSignalSize === "function") { let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB); - if (signalSize < 0){ + if (signalSize < 0) { throw new Error(`Signal ${k} not found\n`); } if (fArr.length < signalSize) { @@ -504,13 +513,13 @@ class WitnessCalculatorCircom2 { throw new Error(`Too many values for input signal ${k}\n`); } } - for (let i=0; i0) { - res.unshift(0); - i--; - } + let i = size - res.length; + while (i > 0) { + res.unshift(0); + i--; + } } return res; } diff --git a/js/witness_calculator.js b/js/witness_calculator.js index ca96f27..c2522d8 100644 --- a/js/witness_calculator.js +++ b/js/witness_calculator.js @@ -17,14 +17,13 @@ limitations under the License. */ -import { flatArray, fnvHash, toArray32, normalize } from "./utils.js"; -import { Scalar, F1Field } from "ffjavascript"; +import {flatArray, fnvHash, toArray32, normalize} from "./utils.js"; +import {Scalar, F1Field} from "ffjavascript"; export default async function builder(code, options) { let instance; let wc; let memory; - options = options || {}; // Only circom 2 implements version lookup through exports in the WASM @@ -33,27 +32,37 @@ export default async function builder(code, options) { let majorVersion = 1; // After Circom 2.0.7, Blaine added exported functions for getting minor and patch versions let minorVersion = 0; - // If we can't lookup the patch version, assume the lowest + // If we can't look up the patch version, assume the lowest let patchVersion = 0; let codeIsWebAssemblyInstance = false; + // If code is already prepared WebAssembly.Instance, we use it directly if (code instanceof WebAssembly.Instance) { instance = code; codeIsWebAssemblyInstance = true; } else { let memorySize = 32767; + + if (options.memorySize) { + // make sure we have int + memorySize = parseInt(options.memorySize); + if (memorySize < 0) { + throw new Error("Invalid memory size"); + } + } + let memoryAllocated = false; - while (!memoryAllocated){ - try{ - memory = new WebAssembly.Memory({initial:memorySize}); + while (!memoryAllocated) { + try { + memory = new WebAssembly.Memory({initial: memorySize}); memoryAllocated = true; - } catch(err){ - if(memorySize === 1){ + } catch (err) { + if (memorySize <= 1) { throw err; } console.warn("Could not allocate " + memorySize * 1024 * 64 + " bytes. This may cause severe instability. Trying with " + memorySize * 1024 * 64 / 2 + " bytes"); - memorySize = Math.floor(memorySize/2); + memorySize = Math.floor(memorySize / 2); } } @@ -67,19 +76,19 @@ export default async function builder(code, options) { "memory": memory }, runtime: { - exceptionHandler: function(code) { + exceptionHandler: function (code) { let err; - if (code == 1) { + if (code === 1) { err = "Signal not found. "; - } else if (code == 2) { + } else if (code === 2) { err = "Too many signals set. "; - } else if (code == 3) { + } else if (code === 3) { err = "Signal already set. "; - } else if (code == 4) { + } else if (code === 4) { err = "Assert Failed. "; - } else if (code == 5) { + } else if (code === 5) { err = "Not enough memory. "; - } else if (code == 6) { + } else if (code === 6) { err = "Input signal array access exceeds the size. "; } else { err = "Unknown error. "; @@ -89,10 +98,10 @@ export default async function builder(code, options) { }, // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports // `printErrorMessage` and `writeBufferMessage`. - printErrorMessage: function() { + printErrorMessage: function () { errStr += getMessage() + "\n"; }, - writeBufferMessage: function() { + writeBufferMessage: function () { const msg = getMessage(); // Any calls to `log()` will always end with a `\n`, so that's when we print and reset if (msg === "\n") { @@ -101,17 +110,17 @@ export default async function builder(code, options) { } else { // If we've buffered other content, put a space in between the items if (msgStr !== "") { - msgStr += " " + msgStr += " "; } // Then append the message to the message we are creating msgStr += msg; } }, - showSharedRWMemory: function() { + showSharedRWMemory: function () { const shared_rw_memory_size = instance.exports.getFieldNumLen32(); const arr = new Uint32Array(shared_rw_memory_size); - for (let j=0; j= 2 && (minorVersion >= 1 || patchVersion >= 7)) { // If we've buffered other content, put a space in between the items if (msgStr !== "") { - msgStr += " " + msgStr += " "; } // Then append the value to the message we are creating const msg = (Scalar.fromArray(arr, 0x100000000).toString()); @@ -128,39 +137,39 @@ export default async function builder(code, options) { console.log(Scalar.fromArray(arr, 0x100000000)); } }, - error: function(code, pstr, a,b,c,d) { + error: function (code, pstr, a, b, c, d) { let errStr; - if (code == 7) { - errStr=p2str(pstr) + " " + wc.getFr(b).toString() + " != " + wc.getFr(c).toString() + " " +p2str(d); - } else if (code == 9) { - errStr=p2str(pstr) + " " + wc.getFr(b).toString() + " " +p2str(c); - } else if ((code == 5)&&(options.sym)) { - errStr=p2str(pstr)+ " " + options.sym.labelIdx2Name[c]; + if (code === 7) { + errStr = p2str(pstr) + " " + wc.getFr(b).toString() + " != " + wc.getFr(c).toString() + " " + p2str(d); + } else if (code === 9) { + errStr = p2str(pstr) + " " + wc.getFr(b).toString() + " " + p2str(c); + } else if ((code === 5) && (options.sym)) { + errStr = p2str(pstr) + " " + options.sym.labelIdx2Name[c]; } else { - errStr=p2str(pstr)+ " " + a + " " + b + " " + c + " " + d; + errStr = p2str(pstr) + " " + a + " " + b + " " + c + " " + d; } console.log("ERROR: ", code, errStr); throw new Error(errStr); }, - log: function(a) { + log: function (a) { console.log(wc.getFr(a).toString()); }, - logGetSignal: function(signal, pVal) { + logGetSignal: function (signal, pVal) { if (options.logGetSignal) { - options.logGetSignal(signal, wc.getFr(pVal) ); + options.logGetSignal(signal, wc.getFr(pVal)); } }, - logSetSignal: function(signal, pVal) { + logSetSignal: function (signal, pVal) { if (options.logSetSignal) { - options.logSetSignal(signal, wc.getFr(pVal) ); + options.logSetSignal(signal, wc.getFr(pVal)); } }, - logStartComponent: function(cIdx) { + logStartComponent: function (cIdx) { if (options.logStartComponent) { options.logStartComponent(cIdx); } }, - logFinishComponent: function(cIdx) { + logFinishComponent: function (cIdx) { if (options.logFinishComponent) { options.logFinishComponent(cIdx); } @@ -169,13 +178,13 @@ export default async function builder(code, options) { }); } - if (typeof instance.exports.getVersion == 'function') { + if (typeof instance.exports.getVersion == "function") { majorVersion = instance.exports.getVersion(); } - if (typeof instance.exports.getMinorVersion == 'function') { + if (typeof instance.exports.getMinorVersion == "function") { minorVersion = instance.exports.getMinorVersion(); } - if (typeof instance.exports.getPatchVersion == 'function') { + if (typeof instance.exports.getPatchVersion == "function") { patchVersion = instance.exports.getPatchVersion(); } @@ -203,9 +212,9 @@ export default async function builder(code, options) { return wc; function getMessage() { - var message = ""; - var c = instance.exports.getMessageChar(); - while ( c != 0 ) { + let message = ""; + let c = instance.exports.getMessageChar(); + while (c !== 0) { message += String.fromCharCode(c); c = instance.exports.getMessageChar(); } @@ -217,11 +226,11 @@ export default async function builder(code, options) { const bytes = []; - for (let i=0; i8[p+i]>0; i++) bytes.push(i8[p+i]); + for (let i = 0; i8[p + i] > 0; i++) bytes.push(i8[p + i]); return String.fromCharCode.apply(null, bytes); } -}; +} class WitnessCalculatorCircom1 { constructor(memory, instance, sanityCheck) { @@ -233,8 +242,8 @@ class WitnessCalculatorCircom1 { const pRawPrime = this.instance.exports.getPRawPrime(); const arr = new Array(this.n32); - for (let i=0; i> 2) + i]; + for (let i = 0; i < this.n32; i++) { + arr[this.n32 - 1 - i] = this.i32[(pRawPrime >> 2) + i]; } this.prime = Scalar.fromArray(arr, 0x100000000); @@ -243,8 +252,8 @@ class WitnessCalculatorCircom1 { this.mask32 = Scalar.fromString("FFFFFFFF", 16); this.NVars = this.instance.exports.getNVars(); - this.n64 = Math.floor((this.Fr.bitLength - 1) / 64)+1; - this.R = this.Fr.e( Scalar.shiftLeft(1 , this.n64*64)); + this.n64 = Math.floor((this.Fr.bitLength - 1) / 64) + 1; + this.R = this.Fr.e(Scalar.shiftLeft(1, this.n64 * 64)); this.RInv = this.Fr.inv(this.R); this.sanityCheck = sanityCheck; } @@ -258,10 +267,10 @@ class WitnessCalculatorCircom1 { const pSigOffset = this.allocInt(); const pFr = this.allocFr(); const keys = Object.keys(input); - keys.forEach( (k) => { + keys.forEach((k) => { const h = fnvHash(k); - const hMSB = parseInt(h.slice(0,8), 16); - const hLSB = parseInt(h.slice(8,16), 16); + const hMSB = parseInt(h.slice(0, 8), 16); + const hLSB = parseInt(h.slice(8, 16), 16); try { this.instance.exports.getSignalOffset32(pSigOffset, 0, hMSB, hLSB); } catch (err) { @@ -269,7 +278,7 @@ class WitnessCalculatorCircom1 { } const sigOffset = this.getInt(pSigOffset); const fArr = flatArray(input[k]); - for (let i=0; i>2]; + return this.i32[p >> 2]; } setInt(p, v) { - this.i32[p>>2] = v; + this.i32[p >> 2] = v; } getFr(p) { const self = this; - const idx = (p>>2); + const idx = (p >> 2); if (self.i32[idx + 1] & 0x80000000) { const arr = new Array(self.n32); - for (let i=0; i> 2)] = 0; self.i32[(p >> 2) + 1] = 0x80000000; const arr = Scalar.toArray(v, 0x100000000); - for (let i=0; i=0) { + if (idx >= 0) { self.i32[(p >> 2) + 2 + i] = arr[idx]; } else { self.i32[(p >> 2) + 2 + i] = 0; @@ -407,8 +415,8 @@ class WitnessCalculatorCircom2 { this.instance.exports.getRawPrime(); const arr = new Uint32Array(this.n32); - for (let i=0; i { + let input_counter = 0; + keys.forEach((k) => { const h = fnvHash(k); - const hMSB = parseInt(h.slice(0,8), 16); - const hLSB = parseInt(h.slice(8,16), 16); + const hMSB = parseInt(h.slice(0, 8), 16); + const hLSB = parseInt(h.slice(8, 16), 16); const fArr = flatArray(input[k]); // Slight deviation from https://github.com/iden3/circom/blob/v2.1.6/code_producers/src/wasm_elements/common/witness_calculator.js // because I don't know when this exported function was added - if (typeof this.instance.exports.getInputSignalSize === 'function') { + if (typeof this.instance.exports.getInputSignalSize === "function") { let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB); - if (signalSize < 0){ + if (signalSize < 0) { throw new Error(`Signal ${k} not found\n`); } if (fArr.length < signalSize) { @@ -445,13 +453,13 @@ class WitnessCalculatorCircom2 { throw new Error(`Too many values for input signal ${k}\n`); } } - for (let i=0; i