Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
b021132
WIP. Run garbage collection during in zkey_new
OBrezhniev May 17, 2024
9254928
Improve memory usage in groth16_prove by limiting scope of vars
OBrezhniev Feb 11, 2025
8e00207
switch to promises and wait for them later. memusage monitoring
OBrezhniev Feb 15, 2025
1df2879
Do zero-copy views when possible in buildABC1
OBrezhniev Sep 13, 2025
f31ef54
Merge branch 'refs/heads/master' into feature/memory-scoping
OBrezhniev Sep 14, 2025
bf8adf5
Merge branch 'refs/heads/feature/sharedArrayBuffers' into feature/mem…
OBrezhniev Sep 14, 2025
b3bc274
Small change in mem stats rendering
OBrezhniev Sep 14, 2025
c09de49
Switch from awaits to promises and wait for them when data is actuall…
OBrezhniev Sep 26, 2025
8e303a3
Improve cli startup time by dynamically loading heavy dependencies on…
OBrezhniev Feb 10, 2026
2b3baee
Added buildABC implementation which runs it in a single wasm thread. …
OBrezhniev Feb 10, 2026
d2b352d
Rebuild with updated ffjavascript ThreadManager (WorkerSlot refactor)
OBrezhniev Jun 20, 2026
0b30b7e
Remove debug console.log calls from groth16 prover build
OBrezhniev Jun 21, 2026
f83c819
Rebuild browser bundle with in-place FFT bit-reversal from ffjavascript
OBrezhniev Jun 21, 2026
1764dfb
Rebuild browser bundle with pairingEq buffer-transfer fix from ffjava…
OBrezhniev Jun 21, 2026
b922d34
Rebuild browser bundles: browser-compatible fastfile + bn128 wasm loa…
OBrezhniev Jun 21, 2026
b4be905
compat: feature-detect crypto, browser-API detection for entropy prom…
OBrezhniev Jun 23, 2026
60e32f4
Rebuild browser bundles: pick up ffjavascript/fastfile browser-field …
OBrezhniev Jun 23, 2026
b3367fd
Rebuild browser bundles: vendored prebuilt wasm from ffjavascript
OBrezhniev Jun 23, 2026
13fdf34
Rebuild browser bundles: lazy worker source + native-preferred base64…
OBrezhniev Jun 23, 2026
d397261
groth16 prove: stream multiexp bases from disk in chunks
OBrezhniev Jun 24, 2026
d745c04
groth16 prove: adaptive streaming buildABC (lower, bounded memory)
OBrezhniev Jun 25, 2026
e678865
Rebuild bundles: explicit 1 GiB BigBuffer page/threshold
OBrezhniev Jun 25, 2026
c714531
groth16 prove: consume FFT inputs in the abc pipeline
OBrezhniev Jun 25, 2026
42490ce
buildABCStream: return flat Uint8Array so the IFFT can consume it
OBrezhniev Jun 25, 2026
dd08a23
buildABC: retire buildABCWASM1, route "wasm1" through streaming
OBrezhniev Jun 25, 2026
5b66077
r1cs info: read only the header, not the constraints
OBrezhniev Jun 28, 2026
90986c4
groth16 prove: expose msmBatching option (auto/enabled/disabled)
OBrezhniev Jul 1, 2026
4c65c39
Rebuild bundles: batch-affine MSM + msmBatching prover option
OBrezhniev Jul 1, 2026
eb59bae
Rebuild bundles: CIOS Montgomery mul (via ffjavascript)
OBrezhniev Jul 1, 2026
414d4ff
Rebuild bundles: square via CIOS mul (via ffjavascript)
OBrezhniev Jul 1, 2026
1cbe850
Rebuild bundles: dedicated CIOS square (via ffjavascript)
OBrezhniev Jul 1, 2026
1b668ed
Rebuild bundles: no-carry CIOS mul (via ffjavascript)
OBrezhniev Jul 2, 2026
3215d46
Rebuild bundles: GLV MSM (via ffjavascript)
OBrezhniev Jul 2, 2026
3fa3d0c
Rebuild bundles: GLS G2 MSM (via ffjavascript)
OBrezhniev Jul 2, 2026
2bdd666
Rebuild bundles: sync to HEAD (via ffjavascript)
OBrezhniev Jul 3, 2026
e02858d
browser_tests: instrumented in-browser proving benchmark
OBrezhniev Jul 3, 2026
452ecba
groth16 prove: retire the multi-threaded buildABC and the wasm/wasm1 …
OBrezhniev Jul 3, 2026
49e1cfc
Rebuild bundles: buildABC cleanup
OBrezhniev Jul 3, 2026
9ff9820
buildABCStream: gather witness values per chunk instead of shipping t…
OBrezhniev Jul 3, 2026
141bf59
Rebuild bundles: gathered-witness buildABCStream
OBrezhniev Jul 3, 2026
f821097
groth16 prove: msmGls option (boolean, default true)
OBrezhniev Jul 3, 2026
cb40252
Rebuild bundles: msmGls option (via ffjavascript)
OBrezhniev Jul 3, 2026
bb26c1d
groth16 prove: msmGlv option; msmGlv/msmGls take "auto" | "disabled"
OBrezhniev Jul 3, 2026
b53db00
Rebuild bundles: msmGlv/msmGls modes (via ffjavascript)
OBrezhniev Jul 3, 2026
fc5391b
Rebuild bundles: bls12-381 GLV (via ffjavascript)
OBrezhniev Jul 3, 2026
e799ba1
Rebuild bundles: FF_NO_BATCH removal (via ffjavascript)
OBrezhniev Jul 3, 2026
cac4a5c
buildABCStream: typed-lane witness gather (per-element memcpy was 8.5…
OBrezhniev Jul 3, 2026
5d53ab0
Rebuild bundles: lane-copy gather
OBrezhniev Jul 3, 2026
c77af73
buildABCStream: gather via Uint32 lanes, not Float64
OBrezhniev Jul 3, 2026
2013992
Rebuild bundles: u32-lane gather
OBrezhniev Jul 3, 2026
71339be
Rollback expose_gc flag
OBrezhniev Jul 3, 2026
adc0448
IIFE build: stub out wasmbuilder/wasmcurves
OBrezhniev Jul 3, 2026
88c08d7
Rebuild IIFE bundles: wasm toolchain stubbed out
OBrezhniev Jul 3, 2026
4be7ec2
Update dependencies: noble-hashes 2, rollup 4, eslint 10, mocha 11, e…
OBrezhniev Jul 4, 2026
169eba4
Rebuild bundles: rollup 4, noble-hashes 2
OBrezhniev Jul 4, 2026
d6ed3de
Pin binfileutils/ffjavascript/r1csfile to commit refs instead of file…
OBrezhniev Jul 4, 2026
4cf02f4
Remove console.time instrumentation from the groth16 prover
OBrezhniev Jul 4, 2026
415e144
Rebuild bundles: silent prover
OBrezhniev Jul 4, 2026
f8c16c2
Re-pin binfileutils/ffjavascript/r1csfile (debug-output cleanup)
OBrezhniev Jul 4, 2026
791e625
Regenerate lockfile against the pinned commit refs
OBrezhniev Jul 4, 2026
9ef1184
Add opt-in memory-usage logging: --memlog CLI flag / memoryLogging op…
OBrezhniev Jul 4, 2026
1aaa760
Rebuild bundles: --memlog flag
OBrezhniev Jul 4, 2026
346a96d
Switch binfileutils/ffjavascript/r1csfile pins to git+https, re-pin
OBrezhniev Jul 4, 2026
5a4c4ed
Re-pin binfileutils (blank-line tidy)
OBrezhniev Jul 4, 2026
d5234b0
Fix dev-dependency audit findings via overrides
OBrezhniev Jul 4, 2026
df52fb7
remove forced gc execution
OBrezhniev Jul 7, 2026
33bc3e9
Merge branch 'master' into feature/memory-scoping
OBrezhniev Jul 8, 2026
a265fd6
Rebuild
OBrezhniev Jul 8, 2026
65d47c3
Fix CI
OBrezhniev Jul 8, 2026
771e45b
Fix CI
OBrezhniev Jul 8, 2026
b2cf078
Re-pin binfileutils/ffjavascript/r1csfile to latest commits
OBrezhniev Jul 8, 2026
a1688b8
test: cover msmBatching/msmGlv/msmGls groth16 prove options
OBrezhniev Jul 8, 2026
2d2183e
fix: guard unguarded logger calls in plonk/fflonk verify and wtns check
OBrezhniev Jul 8, 2026
baa7612
Re-pin binfileutils/ffjavascript/r1csfile to latest commits
OBrezhniev Jul 8, 2026
8118625
Rebuild bundles against re-pinned deps
OBrezhniev Jul 8, 2026
106b6a5
fix: reject a retired/invalid buildABC option instead of silently def…
OBrezhniev Jul 9, 2026
c0f7622
fix getCutPoint lower bound (#630)
teddav Jul 9, 2026
bf2743f
Re-pin deps: worker errors now propagate instead of hanging the prover
OBrezhniev Jul 9, 2026
885facb
fix: drain concurrent prove phases on failure; audit follow-ups
OBrezhniev Jul 9, 2026
d6476a7
fix: close fds on every groth16Prove exit path, not just success
OBrezhniev Jul 9, 2026
291fbc7
Revert "fix: close fds on every groth16Prove exit path, not just succ…
OBrezhniev Jul 9, 2026
b413e40
fix: own the fd open/close lifecycle in groth16Prove's existing try/f…
OBrezhniev Jul 9, 2026
a735cef
simplify: drop the phase-promise drain; observation alone is sufficient
OBrezhniev Jul 9, 2026
d04d5fc
cleanup: trim dead code, stale comments, and always-on memory logging
OBrezhniev Jul 10, 2026
b7b0c6f
perf: re-pin deps for scalar-size partitioned MSM
OBrezhniev Jul 11, 2026
c82f76b
perf: re-pin deps (small-gathered-partition endo cutoff)
OBrezhniev Jul 11, 2026
1d5a433
mem: narrow buffer lifetimes in groth16 setup
OBrezhniev Jul 12, 2026
08a7950
feat: stream zkeys from URLs (browser and Node) via HTTP Range requests
OBrezhniev Jul 12, 2026
e71be10
chore: re-pin deps (worker boot-failure give-up in ffjavascript)
OBrezhniev Jul 12, 2026
1dc10ed
chore: re-pin deps (wasmcurves ESM/Vite migration merged)
OBrezhniev Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions .eslintrc.cjs

This file was deleted.

140 changes: 140 additions & 0 deletions browser_tests/bench.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Instrumented browser groth16 proving benchmark.
//
// Serves the snarkjs IIFE bundle plus a circuit's zkey/witness, drives
// headless Chrome to run snarkjs.groth16.prove in-page, and reports:
// - prove wall time (performance.now in-page)
// - fetch time for zkey+wtns
// - peak in-page JS heap (performance.memory, main thread only)
// - peak renderer-process RSS, sampled externally and scoped to THIS
// Chrome's process tree (captures workers + wasm + ArrayBuffers, which
// the JS heap number misses)
// - in-page proof verification when a verification key is provided
//
// Usage:
// node bench.mjs <circuit.zkey> <witness.wtns> [vkey.json]
// Env:
// BUNDLE=/path/to/snarkjs.js IIFE bundle to serve (default ../build/snarkjs.js)
// PORT=1338 HTTP port
//
// The zkey/wtns are fetched into browser memory ({type:"mem"}), so the peak
// RSS includes them; a 1 GB zkey needs a machine with several GB free.
import { createServer } from "http";
import { createReadStream, statSync } from "fs";
import { execSync } from "child_process";
import { createRequire } from "module";
import { dirname, join } from "path";
import { fileURLToPath } from "url";

const here = dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const puppeteer = require("puppeteer");

const [zkeyPath, wtnsPath, vkPath] = process.argv.slice(2);
if (!zkeyPath || !wtnsPath) {
console.error("usage: node bench.mjs <circuit.zkey> <witness.wtns> [vkey.json]");
process.exit(1);
}
const BUNDLE = process.env.BUNDLE || join(here, "..", "build", "snarkjs.js");
const PORT = parseInt(process.env.PORT || "1338", 10);

const HTML = `<!doctype html><html><head><meta charset=utf8></head><body>
<script src="/snarkjs.js"></script>
<script>
window.__run = async () => {
const log = (m) => window.__log(String(m));
try {
const fetchU8 = async (u) => new Uint8Array(await (await fetch(u)).arrayBuffer());
log("fetching zkey+wtns...");
const t0 = performance.now();
const zkey = await fetchU8("/zkey");
const wtns = await fetchU8("/wtns");
const fetchMs = performance.now() - t0;
log("fetched in " + fetchMs.toFixed(0) + "ms; zkey=" + (zkey.length/1048576).toFixed(0) + "MB");
let peakHeap = 0;
const heapTimer = setInterval(() => { if (performance.memory) peakHeap = Math.max(peakHeap, performance.memory.usedJSHeapSize); }, 50);
const tp = performance.now();
const { proof, publicSignals } = await snarkjs.groth16.prove({type:"mem", data: zkey}, {type:"mem", data: wtns});
const proveMs = performance.now() - tp;
clearInterval(heapTimer);
let verified = "n/a";
try {
const r = await fetch("/vk");
if (r.ok) verified = await snarkjs.groth16.verify(await r.json(), publicSignals, proof);
} catch (e) { /* no vkey served */ }
await window.__done({ proveMs, fetchMs, peakHeapMB: peakHeap/1048576, nPublic: publicSignals.length, verified });
} catch (e) { await window.__done({ error: String(e && e.stack || e) }); }
};
</script></body></html>`;

// ---- static server (whole-file, streamed) ----
const server = createServer((req, res) => {
const url = req.url.split("?")[0];
let file;
if (url === "/" || url === "/prove.html") { res.setHeader("content-type", "text/html"); return res.end(HTML); }
else if (url === "/snarkjs.js") file = BUNDLE;
else if (url === "/zkey") file = zkeyPath;
else if (url === "/wtns") file = wtnsPath;
else if (url === "/vk" && vkPath) { res.setHeader("content-type", "application/json"); return createReadStream(vkPath).pipe(res); }
else { res.statusCode = 404; return res.end("nf"); }
res.setHeader("content-length", statSync(file).size);
createReadStream(file).pipe(res);
});
await new Promise((r) => server.listen(PORT, r));

// ---- renderer-process RSS sampler, scoped to OUR Chrome's descendants ----
// Summing all system renderers would include the user's own browser, so walk
// only the launched browser's process tree and sum --type=renderer RSS.
function rendererRssMB(rootPid) {
try {
const out = execSync("ps -eo pid,ppid,rss,args 2>/dev/null", { encoding: "utf8" });
const rows = out.trim().split("\n").slice(1).map((l) => {
const m = l.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/);
return m ? { pid: +m[1], ppid: +m[2], rss: +m[3], args: m[4] } : null;
}).filter(Boolean);
const childrenOf = new Map();
for (const r of rows) { if (!childrenOf.has(r.ppid)) childrenOf.set(r.ppid, []); childrenOf.get(r.ppid).push(r); }
const byPid = new Map(rows.map((r) => [r.pid, r]));
const seen = new Set([rootPid]); const queue = [rootPid]; let kb = 0;
while (queue.length) {
const p = queue.shift();
for (const c of (childrenOf.get(p) || [])) if (!seen.has(c.pid)) { seen.add(c.pid); queue.push(c.pid); }
}
for (const pid of seen) { const r = byPid.get(pid); if (r && /--type=renderer/.test(r.args)) kb += r.rss; }
return kb / 1024;
} catch { return 0; }
}

const browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox", "--disable-dev-shm-usage", "--enable-precise-memory-info", "--js-flags=--max-old-space-size=12288"],
});
const page = await browser.newPage();
const logs = [];
await page.exposeFunction("__log", (m) => { logs.push(m); });
let result = null;
const done = new Promise((res) => page.exposeFunction("__done", (r) => { result = r; res(r); }));

const browserPid = browser.process().pid;
await page.goto(`http://localhost:${PORT}/prove.html`, { waitUntil: "load" });
await new Promise((r) => setTimeout(r, 500)); // let the renderer settle
const baseRss = rendererRssMB(browserPid); // bundle loaded, before fetch+prove
let peakRss = baseRss;
const rssTimer = setInterval(() => { peakRss = Math.max(peakRss, rendererRssMB(browserPid)); }, 100);

await page.evaluate(() => window.__run());
await done;
clearInterval(rssTimer);

console.log(`\n=== browser groth16 prove: ${zkeyPath} ===`);
for (const l of logs) console.log(" " + l);
if (result.error) console.log(" ERROR:", result.error);
else {
console.log(` prove wall: ${result.proveMs.toFixed(0)} ms`);
console.log(` fetch (zkey+wtns): ${result.fetchMs.toFixed(0)} ms`);
console.log(` peak JS heap: ${result.peakHeapMB.toFixed(0)} MB (main-thread only)`);
console.log(` renderer RSS: base ${baseRss.toFixed(0)} MB -> peak ${peakRss.toFixed(0)} MB (delta ${(peakRss-baseRss).toFixed(0)} MB; page+workers+wasm+ArrayBuffers)`);
console.log(` publicSignals: ${result.nPublic} verified: ${result.verified}`);
}
await browser.close();
server.close();
process.exit(result && !result.error && result.verified !== false ? 0 : 1);
34 changes: 21 additions & 13 deletions browser_tests/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion browser_tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"devDependencies": {
"puppeteer": "^23.5.3",
"ffjavascript": "^0.3.0",
"ffjavascript": "git+https://github.com/iden3/ffjavascript.git#7e0a5850c1f1e9116a93e4845e9ea28bb694300e",
"st": "3.0.0"
}
}
Loading
Loading