@reclaimprotocol/zk-symmetric-crypto
Version:
JS Wrappers for Various ZK Snark Circuits
80 lines (79 loc) • 3.35 kB
JavaScript
import { CONFIG } from "../config.js";
import { serialiseValuesToBits } from "../utils.js";
import { initWorker } from "./node-worker.js";
import { loadCircuitIfRequired, loadExpander, loadProverCircuitIfRequired, makeWorkerPool } from "./utils.js";
import { prove, verify } from './wasm-binding.js';
let wasmInit;
export function makeExpanderZkOperator({ algorithm, fetcher, options: { maxWorkers = 0 } = {} }) {
const { index: id, keySizeBytes } = CONFIG[algorithm];
const workerPool = maxWorkers
? makeWorkerPool(maxWorkers, _initWorker)
: undefined;
let proverLoader;
let circuitLoader;
return {
generateWitness(input) {
const { noncesAndCounters: [{ nonce, counter }] } = input;
const witness = new Uint8Array([
// let's just call this the version flag
1,
...serialiseValuesToBits(algorithm, counter, nonce, input.in, input.out, input.key)
]);
return witness;
},
async groth16Prove(witness, logger) {
const version = readFromWitness(1)[0];
if (version !== 1) {
throw new Error(`Unsupported witness version: ${version}`);
}
// * 8 because we're reading bits
const pubBits = readFromWitness(-keySizeBytes * 8);
const privBits = witness;
await loadProverAsRequired(logger);
if (!workerPool) {
const bytes = prove(id, privBits, pubBits);
return { proof: bytes };
}
const worker = await workerPool.getNext();
const { result: proof } = await (worker.rpc('prove', { args: [id, privBits, pubBits] }));
return { proof };
function readFromWitness(length) {
const result = witness.slice(0, length);
witness = witness.slice(length);
return result;
}
},
async groth16Verify(publicSignals, proof, logger) {
if (!(proof instanceof Uint8Array)) {
throw new Error('Expected proof to be binary');
}
await loadCircuitAsRequired(logger);
const { noncesAndCounters: [{ nonce, counter }], in: inData, out: outData } = publicSignals;
const pubSignals = new Uint8Array(serialiseValuesToBits(algorithm, counter, nonce, inData, outData));
return verify(id, pubSignals, proof);
},
release() {
return workerPool?.release();
}
};
async function loadProverAsRequired(logger) {
wasmInit ||= loadExpander(fetcher, logger);
await wasmInit;
proverLoader ||= loadProverCircuitIfRequired(algorithm, fetcher, logger);
circuitLoader ||= loadCircuitIfRequired(algorithm, fetcher, logger);
await Promise.all([proverLoader, circuitLoader]);
}
async function loadCircuitAsRequired(logger) {
wasmInit ||= loadExpander(fetcher, logger);
await wasmInit;
circuitLoader ||= loadCircuitIfRequired(algorithm, fetcher, logger);
await circuitLoader;
}
}
async function _initWorker() {
const { wasm, module } = await wasmInit;
return initWorker({
module,
initialisationMemory: new Uint8Array(wasm.memory.buffer),
});
}