UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

201 lines 6.24 kB
import { Context } from '../../util/global-context.js'; import { Snarky, initializeBindings } from '../../../bindings.js'; import { parseHexString32 } from '../../../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from '../../util/errors.js'; import { Fp } from '../../../bindings/crypto/finite-field.js'; import { MlBool } from '../../ml/base.js'; // internal API export { snarkContext, asProver, synchronousRunners, generateWitness, constraintSystem, inProver, inAnalyze, inCheckedComputation, inCompile, inCompileMode, gatesFromJson, printGates, summarizeGates, MlConstraintSystem, }; let snarkContext = Context.create({ default: {} }); class MlConstraintSystem { } // helpers to read circuit context function inProver() { return !!snarkContext.get().inProver; } function inCheckedComputation() { let ctx = snarkContext.get(); return !!ctx.inCompile || !!ctx.inProver || !!ctx.inCheckedComputation; } function inCompile() { return !!snarkContext.get().inCompile; } function inAnalyze() { return !!snarkContext.get().inAnalyze; } function inCompileMode() { let ctx = snarkContext.get(); return !!ctx.inCompile || !!ctx.inAnalyze; } // runners for provable code function asProver(f) { if (inCheckedComputation()) { // TODO make this start a "witness block" context Snarky.run.asProver(f); } else { f(); } } async function generateWitness(f, { checkConstraints = true } = {}) { await initializeBindings(); let id = snarkContext.enter({ inCheckedComputation: true }); try { let finish = Snarky.run.enterGenerateWitness(); if (!checkConstraints) Snarky.run.setEvalConstraints(MlBool(false)); await f(); return finish(); } catch (error) { throw prettifyStacktrace(error); } finally { if (!checkConstraints) Snarky.run.setEvalConstraints(MlBool(true)); snarkContext.leave(id); } } async function constraintSystem(f) { await initializeBindings(); let id = snarkContext.enter({ inAnalyze: true, inCheckedComputation: true }); try { let finish = Snarky.run.enterConstraintSystem(); await f(); let cs = finish(); return constraintSystemToJS(cs); } catch (error) { throw prettifyStacktrace(error); } finally { snarkContext.leave(id); } } /** * helpers to run circuits in synchronous tests */ async function synchronousRunners() { await initializeBindings(); function runAndCheckSync(f) { let id = snarkContext.enter({ inCheckedComputation: true }); try { let finish = Snarky.run.enterGenerateWitness(); f(); finish(); } catch (error) { throw prettifyStacktrace(error); } finally { snarkContext.leave(id); } } function constraintSystemSync(f) { let id = snarkContext.enter({ inAnalyze: true, inCheckedComputation: true, }); try { let finish = Snarky.run.enterConstraintSystem(); f(); let cs = finish(); return constraintSystemToJS(cs); } catch (error) { throw prettifyStacktrace(error); } finally { snarkContext.leave(id); } } return { runAndCheckSync, constraintSystemSync }; } function constraintSystemToJS(cs) { // toJson also "finalizes" the constraint system, which means // locking in a potential pending single generic gate let json = Snarky.constraintSystem.toJson(cs); let rows = Snarky.constraintSystem.rows(cs); let digest = Snarky.constraintSystem.digest(cs); let { gates, publicInputSize } = gatesFromJson(json); return { rows, digest, gates, publicInputSize, print() { printGates(gates); }, summary() { return summarizeGates(gates); }, }; } // helpers function gatesFromJson(cs) { let gates = cs.gates.map(({ typ, wires, coeffs: hexCoeffs }) => { let coeffs = hexCoeffs.map((hex) => parseHexString32(hex).toString()); return { type: typ, wires, coeffs }; }); return { publicInputSize: cs.public_input_size, gates }; } // collect a summary of the constraint system function summarizeGates(gates) { let gateTypes = {}; gateTypes['Total rows'] = gates.length; for (let gate of gates) { gateTypes[gate.type] ??= 0; gateTypes[gate.type]++; } return gateTypes; } // print a constraint system function printGates(gates) { for (let i = 0, n = gates.length; i < n; i++) { let { type, wires, coeffs } = gates[i]; console.log(i.toString().padEnd(4, ' '), type.padEnd(15, ' '), coeffsToPretty(type, coeffs).padEnd(30, ' '), wiresToPretty(wires, i)); } console.log(); } let minusRange = Fp.modulus - (1n << 64n); function coeffsToPretty(type, coeffs) { if (coeffs.length === 0) return ''; if (type === 'Generic' && coeffs.length > 5) { let first = coeffsToPretty(type, coeffs.slice(0, 5)); let second = coeffsToPretty(type, coeffs.slice(5)); return `${first} ${second}`; } if (type === 'Poseidon' && coeffs.length > 3) { return `${coeffsToPretty(type, coeffs.slice(0, 3)).slice(0, -1)} ...]`; } let str = coeffs .map((c) => { let c0 = BigInt(c); if (c0 > minusRange) c0 -= Fp.modulus; let cStr = c0.toString(); if (cStr.length > 4) return `${cStr.slice(0, 4)}..`; return cStr; }) .join(' '); return `[${str}]`; } function wiresToPretty(wires, row) { let strWires = []; let n = wires.length; for (let col = 0; col < n; col++) { let wire = wires[col]; if (wire.row === row && wire.col === col) continue; if (wire.row === row) { strWires.push(`${col}->${wire.col}`); } else { strWires.push(`${col}->(${wire.row},${wire.col})`); } } return strWires.join(', '); } //# sourceMappingURL=provable-context.js.map