diff --git a/build/main.cjs b/build/main.cjs index 59df32b..fb62576 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -80,165 +80,154 @@ function toArray32(s,size) { /* globals WebAssembly */ +async function builder(code, options) { + let instance; -async function createWASMInstance(wasmSource, options) { - - let memorySize = 32767; - let memory; - let memoryAllocated = false; - while (!memoryAllocated){ - try{ - memory = new WebAssembly.Memory({initial:memorySize}); - memoryAllocated = true; - } 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); - } - } + // Only circom 2 implements version lookup through exports in the WASM + // We default to `1` and update if we see the `getVersion` export (major version) + // These are updated after the instance is instantiated, assuming the functions are available + 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 + let patchVersion = 0; - const wasmModule = await WebAssembly.compile(wasmSource); - - let errStr = ""; - let msgStr = ""; - - let importsObject = { - env: { - "memory": memory - }, - runtime: { - exceptionHandler: function(code) { - let err; - if (code == 1) { - err = "Signal not found. "; - } else if (code == 2) { - err = "Too many signals set. "; - } else if (code == 3) { - err = "Signal already set. "; - } else if (code == 4) { - err = "Assert Failed. "; - } else if (code == 5) { - err = "Not enough memory. "; - } else if (code == 6) { - err = "Input signal array access exceeds the size. "; - } else { - err = "Unknown error. "; - } - console.error("ERROR: ", code, errStr); - throw new Error(err + errStr); - }, - // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports - // `printErrorMessage` and `writeBufferMessage`. - printErrorMessage: function() { - errStr += getMessage() + "\n"; - }, - 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") { - console.log(msgStr); - msgStr = ""; - } else { - // If we've buffered other content, put a space in between the items - if (msgStr !== "") { - msgStr += " "; - } - // Then append the message to the message we are creating - msgStr += msg; - } - }, - 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]); - - return String.fromCharCode.apply(null, bytes); - } - -} -async function builder(codeOrWasmInstance, options) { - - options = options || {}; - - let instance; + instance = await WebAssembly.instantiate(wasmModule, { + env: { + "memory": memory + }, + runtime: { + exceptionHandler: function(code) { + let err; + if (code == 1) { + err = "Signal not found. "; + } else if (code == 2) { + err = "Too many signals set. "; + } else if (code == 3) { + err = "Signal already set. "; + } else if (code == 4) { + err = "Assert Failed. "; + } else if (code == 5) { + err = "Not enough memory. "; + } else if (code == 6) { + err = "Input signal array access exceeds the size. "; + } else { + err = "Unknown error. "; + } + console.error("ERROR: ", code, errStr); + throw new Error(err + errStr); + }, + // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports + // `printErrorMessage` and `writeBufferMessage`. + printErrorMessage: function() { + errStr += getMessage() + "\n"; + }, + 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") { + console.log(msgStr); + msgStr = ""; + } else { + // If we've buffered other content, put a space in between the items + if (msgStr !== "") { + msgStr += " "; + } + // Then append the message to the message we are creating + msgStr += msg; + } + }, + 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 += " "; + } + // Then append the value to the message we are creating + const msg = (ffjavascript.Scalar.fromArray(arr, 0x100000000).toString()); + msgStr += msg; + } else { + console.log(ffjavascript.Scalar.fromArray(arr, 0x100000000)); + } + }, + 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]; + } else { + errStr=p2str(pstr)+ " " + a + " " + b + " " + c + " " + d; + } + console.log("ERROR: ", code, errStr); + throw new Error(errStr); + }, + log: function(a) { + console.log(wc.getFr(a).toString()); + }, + logGetSignal: function(signal, pVal) { + if (options.logGetSignal) { + options.logGetSignal(signal, wc.getFr(pVal) ); + } + }, + logSetSignal: function(signal, pVal) { + if (options.logSetSignal) { + options.logSetSignal(signal, wc.getFr(pVal) ); + } + }, + logStartComponent: function(cIdx) { + if (options.logStartComponent) { + options.logStartComponent(cIdx); + } + }, + logFinishComponent: function(cIdx) { + if (options.logFinishComponent) { + options.logFinishComponent(cIdx); + } + } + } + }); } if (typeof instance.exports.getVersion == 'function') { @@ -261,8 +250,6 @@ async function builder(codeOrWasmInstance, options) { options.logFinishComponent ); - let wc; - // We explicitly check for major version 2 in case there's a circom v3 in the future if (majorVersion === 2) { wc = new WitnessCalculatorCircom2(instance, sanityCheck); @@ -272,6 +259,25 @@ async function builder(codeOrWasmInstance, options) { } return wc; + function getMessage() { + var message = ""; + var c = instance.exports.getMessageChar(); + while ( c != 0 ) { + message += String.fromCharCode(c); + c = instance.exports.getMessageChar(); + } + return message; + } + + function p2str(p) { + const i8 = new Uint8Array(memory.buffer); + + const bytes = []; + + 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) { diff --git a/js/witness_calculator.js b/js/witness_calculator.js index cb5e7f3..30e4520 100644 --- a/js/witness_calculator.js +++ b/js/witness_calculator.js @@ -20,29 +20,8 @@ limitations under the License. import { flatArray, fnvHash, toArray32, normalize } from "./utils.js"; import { Scalar, F1Field } from "ffjavascript"; - -export async function createWASMInstance(wasmSource, options) { - - let memorySize = 32767; - let memory; - let memoryAllocated = false; - while (!memoryAllocated){ - try{ - memory = new WebAssembly.Memory({initial:memorySize}); - memoryAllocated = true; - } 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); - } - } - - const wasmModule = await WebAssembly.compile(wasmSource); - - let errStr = ""; - let msgStr = ""; +export default async function builder(code, options) { + let instance; // Only circom 2 implements version lookup through exports in the WASM // We default to `1` and update if we see the `getVersion` export (major version) @@ -53,150 +32,142 @@ export async function createWASMInstance(wasmSource, options) { // If we can't lookup the patch version, assume the lowest let patchVersion = 0; - let importsObject = { - env: { - "memory": memory - }, - runtime: { - exceptionHandler: function(code) { - let err; - if (code == 1) { - err = "Signal not found. "; - } else if (code == 2) { - err = "Too many signals set. "; - } else if (code == 3) { - err = "Signal already set. "; - } else if (code == 4) { - err = "Assert Failed. "; - } else if (code == 5) { - err = "Not enough memory. "; - } else if (code == 6) { - err = "Input signal array access exceeds the size. "; - } else { - err = "Unknown error. "; - } - console.error("ERROR: ", code, errStr); - throw new Error(err + errStr); - }, - // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports - // `printErrorMessage` and `writeBufferMessage`. - printErrorMessage: function() { - errStr += getMessage() + "\n"; - }, - 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") { - console.log(msgStr); - msgStr = ""; - } else { - // If we've buffered other content, put a space in between the items - if (msgStr !== "") { - msgStr += " " - } - // Then append the message to the message we are creating - msgStr += msg; - } - }, - 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 += " " - } - // Then append the value to the message we are creating - const msg = (Scalar.fromArray(arr, 0x100000000).toString()); - msgStr += msg; - } else { - console.log(Scalar.fromArray(arr, 0x100000000)); - } - }, - 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]; - } else { - errStr=p2str(pstr)+ " " + a + " " + b + " " + c + " " + d; - } - console.log("ERROR: ", code, errStr); - throw new Error(errStr); - }, - log: function(a) { - console.log(wc.getFr(a).toString()); - }, - logGetSignal: function(signal, pVal) { - if (options.logGetSignal) { - options.logGetSignal(signal, wc.getFr(pVal) ); - } - }, - logSetSignal: function(signal, pVal) { - if (options.logSetSignal) { - options.logSetSignal(signal, wc.getFr(pVal) ); - } - }, - logStartComponent: function(cIdx) { - if (options.logStartComponent) { - options.logStartComponent(cIdx); - } - }, - logFinishComponent: function(cIdx) { - if (options.logFinishComponent) { - options.logFinishComponent(cIdx); + + if (code instanceof WebAssembly.Instance) { + instance = code; + } else { + options = options || {}; + + let memorySize = 32767; + let memory; + let memoryAllocated = false; + while (!memoryAllocated){ + try{ + memory = new WebAssembly.Memory({initial:memorySize}); + memoryAllocated = true; + } 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); } } - }; - - WebAssembly.instantiate(wasmModule, importsObject); - - return wasmModule; - - function getMessage() { - var message = ""; - var c = instance.exports.getMessageChar(); - while ( c != 0 ) { - message += String.fromCharCode(c); - c = instance.exports.getMessageChar(); - } - return message; - } - - function p2str(p) { - const i8 = new Uint8Array(memory.buffer); - const bytes = []; - - for (let i=0; i8[p+i]>0; i++) bytes.push(i8[p+i]); + const wasmModule = await WebAssembly.compile(code); - return String.fromCharCode.apply(null, bytes); - } + let wc; -}; + let errStr = ""; + let msgStr = ""; -export default async function builder(codeOrWasmInstance, options) { - - options = options || {}; - - let instance; + instance = await WebAssembly.instantiate(wasmModule, { + env: { + "memory": memory + }, + runtime: { + exceptionHandler: function(code) { + let err; + if (code == 1) { + err = "Signal not found. "; + } else if (code == 2) { + err = "Too many signals set. "; + } else if (code == 3) { + err = "Signal already set. "; + } else if (code == 4) { + err = "Assert Failed. "; + } else if (code == 5) { + err = "Not enough memory. "; + } else if (code == 6) { + err = "Input signal array access exceeds the size. "; + } else { + err = "Unknown error. "; + } + console.error("ERROR: ", code, errStr); + throw new Error(err + errStr); + }, + // A new way of logging messages was added in Circom 2.0.7 that requires 2 new imports + // `printErrorMessage` and `writeBufferMessage`. + printErrorMessage: function() { + errStr += getMessage() + "\n"; + }, + 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") { + console.log(msgStr); + msgStr = ""; + } else { + // If we've buffered other content, put a space in between the items + if (msgStr !== "") { + msgStr += " " + } + // Then append the message to the message we are creating + msgStr += msg; + } + }, + 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 += " " + } + // Then append the value to the message we are creating + const msg = (Scalar.fromArray(arr, 0x100000000).toString()); + msgStr += msg; + } else { + console.log(Scalar.fromArray(arr, 0x100000000)); + } + }, + 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]; + } else { + errStr=p2str(pstr)+ " " + a + " " + b + " " + c + " " + d; + } + console.log("ERROR: ", code, errStr); + throw new Error(errStr); + }, + log: function(a) { + console.log(wc.getFr(a).toString()); + }, + logGetSignal: function(signal, pVal) { + if (options.logGetSignal) { + options.logGetSignal(signal, wc.getFr(pVal) ); + } + }, + logSetSignal: function(signal, pVal) { + if (options.logSetSignal) { + options.logSetSignal(signal, wc.getFr(pVal) ); + } + }, + logStartComponent: function(cIdx) { + if (options.logStartComponent) { + options.logStartComponent(cIdx); + } + }, + logFinishComponent: function(cIdx) { + if (options.logFinishComponent) { + options.logFinishComponent(cIdx); + } + } + } + }); } if (typeof instance.exports.getVersion == 'function') { @@ -219,8 +190,6 @@ export default async function builder(codeOrWasmInstance, options) { options.logFinishComponent ); - let wc; - // We explicitly check for major version 2 in case there's a circom v3 in the future if (majorVersion === 2) { wc = new WitnessCalculatorCircom2(instance, sanityCheck); @@ -230,6 +199,25 @@ export default async function builder(codeOrWasmInstance, options) { } return wc; + function getMessage() { + var message = ""; + var c = instance.exports.getMessageChar(); + while ( c != 0 ) { + message += String.fromCharCode(c); + c = instance.exports.getMessageChar(); + } + return message; + } + + function p2str(p) { + const i8 = new Uint8Array(memory.buffer); + + const bytes = []; + + for (let i=0; i8[p+i]>0; i++) bytes.push(i8[p+i]); + + return String.fromCharCode.apply(null, bytes); + } }; class WitnessCalculatorCircom1 {