UNPKG

micro-zk-proofs

Version:

Create & verify zero-knowledge SNARK proofs in parallel, using noble cryptography

757 lines 31.7 kB
/** * The code is only used if you plan to run **legacy circom-js programs**. It is unused in WASM. * Minimal witness program executor for circom programs, based on websnark/wasmsnark/snarkjs. * Unsafe: it uses eval, better to be used inside worker threads. * Depends on **monkey-patched BigInt** prototypes due to how circom programs are serialized. * We only patch prototypes before execution. After finishing, patches are reverted. * @module */ import { invert, pow } from '@noble/curves/abstract/modular.js'; import { bn254 as nobleBn254 } from '@noble/curves/bn254.js'; import { bitMask } from '@noble/curves/utils.js'; import { abytes } from '@noble/hashes/utils.js'; import * as P from 'micro-packed'; import {} from "./index.js"; /** * Checks if the provided value is object-like for option/schema bags. * This intentionally matches noble-curves and noble-hashes by using the * `[object Object]` tag instead of rejecting class/proxy/env objects by prototype; * stricter checks caused compatibility reports in proxied environments. * Array, Uint8Array and others are not plain objects. * @param obj - The value to be checked. */ function isPlainObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]'; } function monkeyPatchBigInt() { const methods = { // Equality eq: (a, b) => a === b, neq: (a, b) => a !== b, greaterOrEquals: (a, b) => a >= b, greater: (a, b) => a > b, gt: (a, b) => a > b, lesserOrEquals: (a, b) => a <= b, lesser: (a, b) => a < b, lt: (a, b) => a < b, // Basic math sub: (a, b) => a - b, add: (a, b) => a + b, mul: (a, b) => a * b, div: (a, b) => a / b, mod: (a, b) => a % b, // Fields inverse: (n, modulo) => invert(n, modulo), modPow: (a, power, modulo) => pow(a, power, modulo), // Binary and: (a, b) => a & b, // Old circom serializes `<<` as `.shl(...)`, matching snarkjs v0.2.0's bigint shim. shl: (a, b) => a << BigInt(b), shr: (a, b) => a >> BigInt(b), }; let patched = false; let orig = {}; const proto = BigInt.prototype; const restoreOne = (name, desc) => { if (!desc) delete proto[name]; else Object.defineProperty(proto, name, desc); }; return { patch() { if (patched) throw new Error('bigint: already patched'); const snap = {}; for (const name in methods) { const desc = Object.getOwnPropertyDescriptor(proto, name); if (desc && !desc.configurable) throw new Error(`bigint: cannot patch non-configurable BigInt.prototype.${name}`); // Preserve descriptors: callers may have accessors or own undefined-valued properties here. snap[name] = desc; } try { for (const name in methods) { Object.defineProperty(proto, name, { configurable: true, enumerable: snap[name]?.enumerable || false, value: function (...args) { return methods[name](this, ...args); }, writable: true, }); } } catch (err) { for (const name in snap) restoreOne(name, snap[name]); throw err; } orig = snap; patched = true; }, restore() { if (!patched) throw new Error('bigint: not patched'); for (const name in methods) restoreOne(name, orig[name]); orig = {}; patched = false; }, }; } const selectorStr = (lst) => lst.map((i) => `[${i}]`).join(''); const signalStr = (name, selectors) => name + selectorStr(selectors); // Apply selectors const select = (a, selectors) => { for (const s of selectors) a = a[s]; return a; }; // Old circom can emit setPin/setSignal from templates and functions, including // expression positions such as for headers. It can also emit nested function calls // that trigger child components. Yielding those calls lets the scheduler // trampoline recursive triggers before the caller continues. const ctxYieldCall = /(^|[^\w$.])(ctx\.(?:callFunction|setPin|setSignal)\()/g; const jsIgnored = /("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|`(?:\\[\s\S]|[^`\\])*`|\/\/[^\n\r]*(?:\r\n?|\n|$)|\/\*[\s\S]*?\*\/)/g; const yieldCtxCalls = (src) => { const code = (chunk) => chunk.replace(ctxYieldCall, '$1yield $2'); let out = ''; let last = 0; for (const match of src.matchAll(jsIgnored)) { out += code(src.slice(last, match.index)) + match[0]; last = match.index + match[0].length; } return out + code(src.slice(last)); }; const codeToGenerator = (src) => yieldCtxCalls(src.replace(/function\s*\(\s*ctx\s*\)\s*\{/, 'function*(ctx) {')); export const __TESTS = /* @__PURE__ */ Object.freeze({ yieldCtxCalls: yieldCtxCalls, }); /** * Builds a witness generator for a legacy circom-js circuit JSON. * @param circJson - Circom circuit JSON artifact. * @returns Function that executes the circuit and returns the witness. * @example * Build a witness runner from a circom JSON circuit artifact. * ```ts * import { generateWitness } from 'micro-zk-proofs/witness.js'; * // Addition circuit: witness output is one, a + b, b, a. * const circuitJson = { * nVars: 4, * nInputs: 2, * nOutputs: 1, * nSignals: 4, * templates: { * Main: `function(ctx) { * ctx.setSignal( * "out", * [], * bigInt(ctx.getSignal("a", [])).add(bigInt(ctx.getSignal("b", []))).mod(__P__) * ); * }`, * }, * functions: {}, * components: [{ name: 'main', params: {}, template: 'Main', inputSignals: 2 }], * signals: [ * { names: ['one'], triggerComponents: [] }, * { names: ['main.out'], triggerComponents: [] }, * { names: ['main.b'], triggerComponents: [0] }, * { names: ['main.a'], triggerComponents: [0] }, * ], * signalName2Idx: { one: 0, 'main.out': 1, 'main.b': 2, 'main.a': 3 }, * }; * const witness = generateWitness(circuitJson)({ a: '33', b: '34' }); * // [1n, 67n, 34n, 33n] * ``` */ export function generateWitness(circJson) { if (!isPlainObject(circJson)) throw new TypeError('"circJson" expected object, got type=' + typeof circJson); if (!Array.isArray(circJson.signals)) throw new TypeError('"circJson.signals" expected array, got type=' + typeof circJson.signals); if (!Array.isArray(circJson.components)) throw new TypeError('"circJson.components" expected array, got type=' + typeof circJson.components); if (!isPlainObject(circJson.templates)) throw new TypeError('"circJson.templates" expected object, got type=' + typeof circJson.templates); if (!isPlainObject(circJson.functions)) throw new TypeError('"circJson.functions" expected object, got type=' + typeof circJson.functions); const P = nobleBn254.fields.Fr.ORDER; const MASK = bitMask(nobleBn254.fields.Fr.BITS); const signals = circJson.signals; const components = circJson.components; const templates = {}; // Bind P & MASK directly into templates/functions, so we see dependency for (let t in circJson.templates) { templates[t] = new Function('bigInt', '__P__', '__MASK__', 'return ' + codeToGenerator(circJson.templates[t]))(BigInt, P, MASK); } const functions = {}; for (let f in circJson.functions) { functions[f] = { params: circJson.functions[f].params, func: new Function('bigInt', '__P__', '__MASK__', 'return ' + codeToGenerator(circJson.functions[f].func))(BigInt, P, MASK), }; } function inputIdx(i) { if (i >= circJson.nInputs) throw new Error('Accessing an invalid input: ' + i); // Witness slot 0 is the constant one, so declared inputs start after the output slots. return circJson.nOutputs + 1 + i; } function getSignalIdx(name) { if (circJson.signalName2Idx[name] !== undefined) return circJson.signalName2Idx[name]; // signalNames() also queries raw witness indices when building error messages. if (!isNaN(name)) return Number(name); throw new Error('Invalid signal identifier: ' + name); } const signalNames = (i) => signals[getSignalIdx(i)].names.join(', '); const patcher = monkeyPatchBigInt(); return function (input) { const witness = new Array(circJson.nSignals); let currentComponent; let scopes = []; // scope stack const notInitSignals = {}; const callFrameTag = Symbol('callFrame'); // Ready component frames preserve old depth-first trigger order without growing // the JS call stack on long generated chains. const pendingComponents = []; const execFrames = []; let draining = false; let stepping = false; const isCallFrame = (value) => value !== undefined && value.tag === callFrameTag; function triggerComponent(c) { notInitSignals[c]--; const oldComponent = currentComponent; currentComponent = components[c].name; const template = components[c].template; const newScope = {}; for (let p in components[c].params) newScope[p] = components[c].params[p]; execFrames.push({ name: components[c].name, scope: newScope, iter: templates[template](ctx), ready: [], resume: undefined, hasResume: false, done: false, value: undefined, }); currentComponent = oldComponent; } function activeReady() { return execFrames.length > 0 ? execFrames[execFrames.length - 1].ready : pendingComponents; } function removeQueued(c) { let pendingIdx = pendingComponents.indexOf(c); while (pendingIdx >= 0) { pendingComponents.splice(pendingIdx, 1); pendingIdx = pendingComponents.indexOf(c); } for (const frame of execFrames) { let idx = frame.ready.indexOf(c); while (idx >= 0) { frame.ready.splice(idx, 1); idx = frame.ready.indexOf(c); } } } function queueReady(c) { // A generated parent can re-trigger a component already queued by an input // alias. Old recursion runs it at the later trigger point, so move it there. removeQueued(c); activeReady().push(c); } function stepFrame() { const frame = execFrames[execFrames.length - 1]; const oldComponent = currentComponent; const oldScope = scopes; const wasStepping = stepping; currentComponent = frame.name; scopes = [scopes[0], frame.scope]; stepping = true; try { const res = frame.hasResume ? frame.iter.next(frame.resume) : frame.iter.next(); frame.hasResume = false; if (!res.done) { if (isCallFrame(res.value)) { res.value.frame.returnTo = frame; execFrames.push(res.value.frame); return; } frame.resume = res.value; frame.hasResume = true; return; } frame.done = true; frame.value = res.value; execFrames.pop(); if (frame.returnTo !== undefined) { frame.returnTo.resume = frame.value; frame.returnTo.hasResume = true; } if (frame.ready.length > 0) activeReady().unshift(...frame.ready); } finally { stepping = wasStepping; scopes = oldScope; currentComponent = oldComponent; } } function drainStep() { const ready = activeReady(); if (ready.length == 0) return stepFrame(); const c = ready.shift(); if (notInitSignals[c] >= 0) triggerComponent(c); } function drainFrame(frame) { while (!frame.done) drainStep(); return frame.value; } // Process ready components until none remain in the active frame. The notInitSignals guard // (>= 0) skips duplicates: triggerComponent decrements to -1 on first call, so // any component pushed twice will be a no-op on the second dequeue. function drainPendingComponents(force = false) { if (stepping) return; if (draining && !force) return; const wasDraining = draining; draining = true; try { while (activeReady().length > 0 || execFrames.length > 0) drainStep(); } finally { draining = wasDraining; } } function setSignalFullName(fullName, value) { const sId = getSignalIdx(fullName); let firstInit = false; if (witness[sId] === undefined) firstInit = true; witness[sId] = BigInt(value); const triggers = signals[sId].triggerComponents; for (let i = 0; i < signals[sId].triggerComponents.length; i++) { const idCmp = triggers[i]; if (firstInit) notInitSignals[idCmp]--; } const seen = new Set(); for (let i = 0; i < triggers.length; i++) { const c = triggers[i]; // Old circom can alias many inputs of the same component to one signal, // producing duplicate trigger ids. Decrement every alias first, then // queue each ready component once in old first-trigger order; repeated // queueReady calls repeatedly scan queues and can move duplicates. if (notInitSignals[c] == 0 && !seen.has(c)) { seen.add(c); queueReady(c); } } if (!stepping) drainPendingComponents(); return witness[sId]; } function getSignalFullName(name) { const id = getSignalIdx(name); // Circom-generated code can make several components ready at once through // aliasing. Drain only when this read actually needs pending work; eager // drains can run a child before the current parent reaches an earlier write. if (witness[id] === undefined && activeReady().length > 0) drainPendingComponents(true); if (witness[id] === undefined) throw new Error('Signal not initialized: ' + name); return witness[id]; } const cName = (name) => (name == 'one' ? 'one' : currentComponent + '.' + name); // Minimal API that used inside evaluated code const ctx = { // Pins setPin(compName, compSel, sigName, sigSel, value) { const name = signalStr(cName(compName), compSel) + '.' + signalStr(sigName, sigSel); setSignalFullName(name, value); }, getPin(compName, componentSels, sigName, sigSel) { const name = signalStr(cName(compName), componentSels) + '.' + signalStr(sigName, sigSel); return getSignalFullName(name); }, // Vars setVar(name, sels, value) { const scope = scopes[scopes.length - 1]; if (sels.length == 0) { scope[name] = value; } else { if (scope[name] === undefined) scope[name] = []; let cur = scope[name]; for (let i = 0; i < sels.length - 1; i++) { if (cur[sels[i]] === undefined) cur[sels[i]] = []; cur = cur[sels[i]]; } cur[sels[sels.length - 1]] = value; } return value; }, getVar(name, sels) { for (let i = scopes.length - 1; i >= 0; i--) if (scopes[i][name] !== undefined) return select(scopes[i][name], sels); throw new Error('Variable not defined: ' + name); }, // Signals setSignal(name, sels, value) { setSignalFullName(signalStr(currentComponent ? currentComponent + '.' + name : name, sels), value); }, getSignal(name, sels) { return getSignalFullName(signalStr(cName(name), sels)); }, // Utils callFunction(name, params) { const newScope = {}; for (let p = 0; p < functions[name].params.length; p++) newScope[functions[name].params[p]] = params[p]; const frame = { name: currentComponent, scope: newScope, iter: functions[name].func(ctx), ready: [], resume: undefined, hasResume: false, done: false, value: undefined, }; if (stepping) return { tag: callFrameTag, frame }; execFrames.push(frame); return drainFrame(frame); }, assert(a, b, errStr = '') { a = BigInt(a); b = BigInt(b); if (a === b) return; throw new Error(`Constraint doesn't match ${currentComponent}: ${errStr} -> ${a} != ${b}`); }, }; patcher.patch(); try { // Processing for (const c in components) notInitSignals[c] = components[c].inputSignals; ctx.setSignal('one', [], BigInt(1)); // Queue all components that are already fully initialised (zero remaining inputs) // and drain the queue iteratively rather than triggering them inline. This avoids // a call-stack overflow when complex circuits have long inter-template chains. for (let c in notInitSignals) if (notInitSignals[c] == 0) queueReady(c); drainPendingComponents(); // Circuit JSON inputs are own fields; prototypes may carry unrelated app metadata. for (const s of Object.keys(input)) { currentComponent = 'main'; const stack = [{ selectors: [], values: input[s] }]; while (stack.length) { const { selectors, values } = stack.pop(); if (!Array.isArray(values)) { if (values === undefined) throw new Error('Signal not defined:' + s); ctx.setSignal(s, selectors, BigInt(values)); continue; } for (let j = values.length - 1; j >= 0; j--) { stack.push({ selectors: [...selectors, `${j}`], values: values[j] }); } } } for (let i = 0; i < circJson.nInputs; i++) { const idx = inputIdx(i); if (witness[idx] === undefined) throw new Error('Input Signal not assigned: ' + signalNames(idx)); } for (let i = 0; i < witness.length; i++) if (witness[i] === undefined) throw new Error('Signal not assigned: ' + signalNames(i)); return witness.slice(0, circJson.nVars); } finally { patcher.restore(); } }; } /** * Binary coders and parsers for Circom2 artifacts. * @param curve - Curve pair used for field sizing and point decoding. * @returns R1CS, witness, and zkey coders plus parse helpers. * @example * Build the coders once, then use them to parse and encode Circom2 artifacts. * ```ts * const { bn254 } = await import('@noble/curves/bn254.js'); * const coders = getCoders(bn254); * const bytes = coders.binWitness.encode([1n, 2n]); * coders.binWitness.decode(bytes); * ``` */ export const getCoders = (curve) => { if (!isPlainObject(curve)) throw new TypeError('"curve" expected curve object, got type=' + typeof curve); if (!isPlainObject(curve.fields)) throw new TypeError('"curve.fields" expected object, got type=' + typeof curve.fields); if (!isPlainObject(curve.fields.Fr)) throw new TypeError('"curve.fields.Fr" expected object, got type=' + typeof curve.fields.Fr); const field = curve.fields.Fr; // NOTE: we need to pass field here, even if bigints are variable size, they are fixed to field bytes! const fieldBytes = field.BYTES; const fieldCoder = P.bigint(fieldBytes, true, false); const Header = P.struct({ prime: P.prefix(P.U32LE, fieldCoder), // TODO: verify that exactly same as field.ORDER? nWires: P.U32LE, // Total Number of wires including ONE signal (Index 0). nPubOut: P.U32LE, // Total Number of wires public output wires. They should be starting at idx 1 nPubIn: P.U32LE, // Total Number of wires public input wires. They should be starting just after the public output nPrvIn: P.U32LE, // Total Number of wires private input wires. They should be starting just after the public inputs nLables: P.U64LE, // Total Number of wires private input wires. They should be starting just after the public inputs mConstraints: P.U32LE, // Total Number of constraints }); const constraintDict = { encode: (from) => { if (!Array.isArray(from)) throw new Error('array expected'); const to = {}; for (const item of from) { if (!Array.isArray(item) || item.length !== 2) throw new Error(`array of two elements expected`); const [key, value] = item; if (Object.prototype.hasOwnProperty.call(to, key)) throw new Error(`key(${key}) appears twice in constraint`); to[key] = value; } return to; }, decode: (to) => { if (to === null || typeof to !== 'object' || Array.isArray(to)) throw new Error(`expected constraint object, got ${to}`); return Object.entries(to).map(([key, value]) => { // Object.entries() stringifies numeric R1CS signal ids; U32LE needs the number back. if (!/^(0|[1-9][0-9]*)$/.test(key)) throw new Error(`expected uint32 constraint key, got ${key}`); const n = Number(key); if (!Number.isSafeInteger(n) || n < 0 || n > 0xffffffff) throw new Error(`expected uint32 constraint key, got ${key}`); return [n, value]; }); }, }; const Constraint = P.apply(P.array(P.U32LE, P.tuple([P.U32LE, fieldCoder])), constraintDict); // A*B-C = 0 const Constraints = P.array(null, P.tuple([Constraint, Constraint, Constraint])); const WireMap = P.array(null, P.U64LE); // prefix() emits JS byte lengths, while Circom section headers serialize them as u64. const sectionLen = P.apply(P.U64LE, P.coders.numberBigint); const section = (inner) => P.prefix(sectionLen, inner); const empty = P.bytes(null); const R1CSSection = P.mappedTag(P.U32LE, { header: [0x01, section(Header)], constraint: [0x02, section(Constraints)], wire2label: [0x03, section(WireMap)], // not implemented: ultra-plonk customGatesList: [0x04, section(empty)], customGatesApplication: [0x05, section(empty)], }); const R1CS = P.struct({ magic: P.magic(P.string(4), 'r1cs'), version: P.U32LE, sections: P.array(P.U32LE, R1CSSection), }); const binWitness = P.array(null, fieldCoder); const WTNSHeader = P.struct({ prime: P.prefix(P.U32LE, fieldCoder), size: P.U32LE, }); const WTNSSection = P.mappedTag(P.U32LE, { header: [0x01, section(WTNSHeader)], witness: [0x02, section(P.array(null, fieldCoder))], }); const WTNS = P.struct({ magic: P.magic(P.string(4), 'wtns'), version: P.U32LE, sections: P.array(P.U32LE, WTNSSection), }); const G1 = P.tuple([fieldCoder, fieldCoder]); const G2 = P.tuple([fieldCoder, fieldCoder, fieldCoder, fieldCoder]); const ZKeyHeader = P.map(P.U32LE, { groth16: 1, }); const ZKeyHeaderGroth = P.struct({ n8q: P.U32LE, q: fieldCoder, n8r: P.U32LE, r: fieldCoder, nVars: P.U32LE, nPublic: P.U32LE, domainSize: P.U32LE, vk_alpha_1: G1, vk_beta_1: G1, vk_beta_2: G2, vk_gamma_2: G2, vk_delta_1: G1, vk_delta_2: G2, }); const ZKeyCoeff = P.struct({ matrix: P.U32LE, constraint: P.U32LE, signal: P.U32LE, value: fieldCoder, }); const ZKeySection = P.mappedTag(P.U32LE, { header: [1, section(ZKeyHeader)], headerGroth: [2, section(ZKeyHeaderGroth)], IC: [3, section(P.array(null, G1))], ccoefs: [4, section(P.array(P.U32LE, ZKeyCoeff))], A: [5, section(P.array(null, G1))], B1: [6, section(P.array(null, G1))], B2: [7, section(P.array(null, G2))], C: [8, section(P.array(null, G1))], hExps: [9, section(P.array(null, G1))], Contributions: [10, section(P.bytes(null))], }); const ZKeyRaw = P.struct({ magic: P.magic(P.string(4), 'zkey'), version: P.U32LE, sections: P.array(P.U32LE, ZKeySection), }); const getCircuitInfo = (bytes) => { bytes = abytes(bytes, undefined, 'bytes'); const data = R1CS.decode(bytes); const constraints = data.sections.find((i) => i.TAG === 'constraint'); if (!constraints) throw new Error('R1CS: cannot find constraints'); const header = data.sections.find((i) => i.TAG === 'header'); if (!header) throw new Error('R1CS: cannot find header'); if (header.data.prime !== field.ORDER) throw new Error('R1CS: wrong field order'); return { nVars: header.data.nWires, nPubInputs: header.data.nPubIn, nOutputs: header.data.nPubOut, constraints: constraints.data, }; }; function parseZKey(zkey) { zkey = abytes(zkey, undefined, 'zkey'); const { Fr, Fp } = curve.fields; // Montgomery encoding of field elements const fieldFromMont = (f, is1) => { const Rr = f.pow(BigInt(2), BigInt(f.BYTES * 8)); const RRi = f.inv(Rr); const RRi2 = f.mul(RRi, RRi); // G1/G2 coordinates carry one Montgomery factor; coefficient field elements need two. return (x) => f.mul(x, is1 ? RRi : RRi2); }; const is0 = (x) => x === BigInt(0); const convFr2 = fieldFromMont(Fr, false); const convFp = fieldFromMont(Fp, true); const convG1 = ([x, y]) => is0(x) && is0(y) ? [BigInt(0), BigInt(1), BigInt(0)] : [convFp(x), convFp(y), BigInt(1)]; // [ [ 0n, 0n ], [ 0n, 0n ], [ 1n, 0n ] ], -> [ [ 0n, 0n ], [ 1n, 0n ], [ 0n, 0n ] ], const convG2 = ([xc0, xc1, yc0, yc1]) => is0(xc0) && is0(xc1) && is0(yc0) && is0(yc1) ? [ [BigInt(0), BigInt(0)], [BigInt(1), BigInt(0)], [BigInt(0), BigInt(0)], ] : [ [convFp(xc0), convFp(xc1)], [convFp(yc0), convFp(yc1)], [BigInt(1), BigInt(0)], ]; const data = ZKeyRaw.decode(zkey); function getByTag(sections, tag) { const v = sections.find((i) => i.TAG === tag); if (!v) throw new Error('ZKey: cannot find ' + String(tag)); return v.data; } function collect(sections, ks) { const out = {}; for (const k of ks) out[k] = getByTag(sections, k); return out; } const res = collect(data.sections, [ 'header', 'headerGroth', 'IC', 'ccoefs', 'A', 'B1', 'B2', 'C', 'hExps', ]); // Same format as verification key const json = { protocol: res.header, ...res.headerGroth, vk_alpha_1: convG1(res.headerGroth.vk_alpha_1), vk_beta_1: convG1(res.headerGroth.vk_beta_1), vk_delta_1: convG1(res.headerGroth.vk_delta_1), vk_beta_2: convG2(res.headerGroth.vk_beta_2), vk_delta_2: convG2(res.headerGroth.vk_delta_2), vk_gamma_2: convG2(res.headerGroth.vk_gamma_2), power: Math.log2(res.headerGroth.domainSize), IC: res.IC.map(convG1), ccoefs: res.ccoefs.map((i) => ({ ...i, value: convFr2(i.value) })), A: res.A.map(convG1), B1: res.B1.map(convG1), B2: res.B2.map(convG2), // snarkjs zkeys omit the leading zero C-query entries for public signals. C: new Array(res.headerGroth.nPublic + 1).fill(null).concat(res.C.map(convG1)), hExps: res.hExps.map(convG1), }; // Our format (old snarkjs compat) const pkey = { protocol: 'groth', nVars: json.nVars, nPublic: json.nPublic, domainSize: json.domainSize, domainBits: json.power, // Polynominals (instead polsA/polsB/polsC) ccoefs: json.ccoefs, // changed // A: json.A, B1: json.B1, B2: json.B2, C: json.C, // vk_alfa_1: json.vk_alpha_1, vk_beta_1: json.vk_beta_1, vk_delta_1: json.vk_delta_1, vk_beta_2: json.vk_beta_2, vk_delta_2: json.vk_delta_2, // hExps: json.hExps, }; const vkey = { protocol: 'groth', nPublic: json.nPublic, IC: json.IC, vk_alfa_1: json.vk_alpha_1, vk_beta_2: json.vk_beta_2, vk_gamma_2: json.vk_gamma_2, vk_delta_2: json.vk_delta_2, }; return { json, pkey, vkey }; } return { R1CS, binWitness, WTNS, getCircuitInfo, ZKeyRaw, parseZKey }; }; //# sourceMappingURL=witness.js.map