UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

187 lines 7.12 kB
import 'reflect-metadata'; import { Snarky, initializeBindings } from '../../bindings.js'; import { MlFieldArray, MlFieldConstArray } from '../ml/fields.js'; import { withThreadPool } from '../../bindings.js'; import { Provable } from '../provable/provable.js'; import { snarkContext, gatesFromJson } from '../provable/core/provable-context.js'; import { prettifyStacktrace, prettifyStacktracePromise } from '../util/errors.js'; // external API export { public_, circuitMain, Circuit, Keypair, Proof, VerificationKey }; class Circuit { /** * Generates a proving key and a verification key for this circuit. * @example * ```ts * const keypair = await MyCircuit.generateKeypair(); * ``` */ static async generateKeypair(lazyMode = false) { let main = mainFromCircuitData(this._main); let publicInputSize = this._main.publicInputType.sizeInFields(); await initializeBindings(); return prettifyStacktracePromise(withThreadPool(async () => { let keypair = Snarky.circuit.compile(main, publicInputSize, lazyMode); return new Keypair(keypair); })); } /** * Proves a statement using the private input, public input, and the {@link Keypair} of the circuit. * @example * ```ts * const keypair = await MyCircuit.generateKeypair(); * const proof = await MyCircuit.prove(privateInput, publicInput, keypair); * ``` */ static async prove(privateInput, publicInput, keypair) { let main = mainFromCircuitData(this._main, privateInput); let publicInputSize = this._main.publicInputType.sizeInFields(); let publicInputFields = this._main.publicInputType.toFields(publicInput); await initializeBindings(); return prettifyStacktracePromise(withThreadPool(async () => { let proof = Snarky.circuit.prove(main, publicInputSize, MlFieldConstArray.to(publicInputFields), keypair.value); return new Proof(proof); })); } /** * Verifies a proof using the public input, the proof, and the initial {@link Keypair} of the circuit. * @example * ```ts * const keypair = await MyCircuit.generateKeypair(); * const proof = await MyCircuit.prove(privateInput, publicInput, keypair); * const isValid = await MyCircuit.verify(publicInput, keypair.vk, proof); * ``` */ static async verify(publicInput, verificationKey, proof) { let publicInputFields = this._main.publicInputType.toFields(publicInput); await initializeBindings(); return prettifyStacktracePromise(withThreadPool(async () => Snarky.circuit.verify(MlFieldConstArray.to(publicInputFields), proof.value, verificationKey.value))); } } class Keypair { constructor(value) { this.value = value; } verificationKey() { return new VerificationKey(Snarky.circuit.keypair.getVerificationKey(this.value)); } /** * Returns a low-level JSON representation of the {@link Circuit} from its {@link Keypair}: * a list of gates, each of which represents a row in a table, with certain coefficients and wires to other (row, column) pairs * @example * ```ts * const keypair = await MyCircuit.generateKeypair(); * const json = MyProvable.witnessFromKeypair(keypair); * ``` */ constraintSystem() { try { return gatesFromJson(Snarky.circuit.keypair.getConstraintSystemJSON(this.value)).gates; } catch (error) { throw prettifyStacktrace(error); } } } /** * Proofs can be verified using a {@link VerificationKey} and the public input. */ class Proof { constructor(value) { this.value = value; } } /** * Part of the circuit {@link Keypair}. A verification key can be used to verify a {@link Proof} when you provide the correct public input. */ class VerificationKey { constructor(value) { this.value = value; } } function public_(target, _key, index) { // const fieldType = Reflect.getMetadata('design:paramtypes', target, key); if (target._public === undefined) { target._public = []; } target._public.push(index); } function mainFromCircuitData(data, privateInput) { return function main(publicInputFields) { let id = snarkContext.enter({ inCheckedComputation: true }); try { let publicInput = data.publicInputType.fromFields(MlFieldArray.from(publicInputFields)); let privateInput_ = Provable.witness(data.privateInputType, () => privateInput); data.main(publicInput, privateInput_); } finally { snarkContext.leave(id); } }; } function circuitMain(target, propertyName, _descriptor) { const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyName); const numArgs = paramTypes.length; const publicIndexSet = new Set(target._public); const witnessIndexSet = new Set(); for (let i = 0; i < numArgs; ++i) { if (!publicIndexSet.has(i)) witnessIndexSet.add(i); } target._main = { main(publicInput, privateInput) { let args = []; for (let i = 0; i < numArgs; ++i) { let nextInput = publicIndexSet.has(i) ? publicInput : privateInput; args.push(nextInput.shift()); } return target[propertyName].apply(target, args); }, publicInputType: provableFromTuple(Array.from(publicIndexSet).map((i) => paramTypes[i])), privateInputType: provableFromTuple(Array.from(witnessIndexSet).map((i) => paramTypes[i])), }; } // TODO support auxiliary data function provableFromTuple(inputTypes) { let types = inputTypes.map((t) => ('provable' in t ? t.provable : t)); return { sizeInFields: () => { return types.reduce((acc, type) => acc + type.sizeInFields(), 0); }, toFields: (t) => { if (t.length !== types.length) { throw new Error(`typOfArray: Expected ${types.length}, got ${t.length}`); } let res = []; for (let i = 0; i < t.length; ++i) { res.push(...types[i].toFields(t[i])); } return res; }, toAuxiliary() { return []; }, fromFields: (xs) => { let offset = 0; let res = []; types.forEach((typ) => { const n = typ.sizeInFields(); res.push(typ.fromFields(xs.slice(offset, offset + n))); offset += n; }); return res; }, check(xs) { types.forEach((typ, i) => typ.check(xs[i])); }, toCanonical(x) { return types.map((typ, i) => Provable.toCanonical(typ, x[i])); }, toValue(x) { return types.map((typ, i) => typ.toValue(x[i])); }, fromValue(x) { return types.map((typ, i) => typ.fromValue(x[i])); }, }; } //# sourceMappingURL=circuit.js.map