o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
169 lines • 6.87 kB
JavaScript
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, summarizeGates, printGates, } from '../provable/core/provable-context.js';
import { prettifyStacktrace, prettifyStacktracePromise } from '../util/errors.js';
import { provablePure } from '../provable/types/provable-derivers.js';
// external API
export { ZkFunction, KimchiProof, KimchiVerificationKey };
function ZkFunction(config) {
const publicInputType = provablePure(config.publicInputType ?? undefined);
const hasPublicInput = config.publicInputType !== undefined;
let _keypair;
return {
/**
* Generates and stores a proving key and a verification key for this circuit(ZkFunction).
*
* @returns The generated verification key.
*
* @example
* ```ts
* const { verificationKey } = await zkf.compile();
* ```
* @warning Must be called before `prove` or `analyzeMethod`.
*/
async compile() {
const main = mainFromCircuitData(config);
const publicInputSize = publicInputType.sizeInFields();
const lazyMode = config.lazyMode ?? false;
await initializeBindings();
_keypair = await prettifyStacktracePromise(withThreadPool(async () => {
return Snarky.circuit.compile(main, publicInputSize, lazyMode);
}));
const verificationKey = new KimchiVerificationKey(Snarky.circuit.keypair.getVerificationKey(_keypair));
return { verificationKey };
},
/**
* Returns a low-level JSON representation of the constraint system (gates)
*
* @throws If compile() has not been called yet.
*
* @example
* ```ts
* await zkf.compile();
* const cs = zkf.analyzeMethod();
* console.log(cs);
* ```
*/
analyzeMethod() {
if (!_keypair)
throw new Error('Cannot find prover artifacts. Please call compile() first!');
try {
let { gates, publicInputSize } = gatesFromJson(Snarky.circuit.keypair.getConstraintSystemJSON(_keypair));
return {
rows: gates.length,
gates,
publicInputSize,
print() {
printGates(gates);
},
summary() {
return summarizeGates(gates);
},
};
}
catch (error) {
throw prettifyStacktrace(error);
}
},
/**
* Proves a statement using the public input and private inputs of the circuit(ZkFunction).
*
* @param publicInput The public input to the circuit if it exists.
* @param privateInputs The private inputs to the circuit.
* @returns The generated proof.
*
* @throws If `compile` has not been called.
*
* @example
* ```ts
* const { verificationKey } = await zkf.compile();
* const proof = await zkf.prove(publicInput, privateInput1, privateInput2);
* ```
*/
async prove(...args) {
if (!_keypair)
throw new Error('Cannot find prover artifacts. Please call compile() first!');
const publicInput = hasPublicInput ? args[0] : undefined;
const privateInputs = (hasPublicInput ? args.slice(1) : args);
const publicInputSize = publicInputType.sizeInFields();
const publicInputFields = publicInputType.toFields(publicInput);
const main = mainFromCircuitData(config, privateInputs);
await initializeBindings();
return withThreadPool(async () => {
const proof = Snarky.circuit.prove(main, publicInputSize, MlFieldConstArray.to(publicInputFields), _keypair);
return new KimchiProof(proof, publicInputFields);
});
},
/**
* Verifies a proof using the verification key of the circuit(ZkFunction).
*
* @param proof The proof to verify.
* @param verificationKey The key to verify against.
*
* @returns `true` if the proof is valid, otherwise `false`.
*
* @example
* ```ts
* const { verificationKey } = await zkf.compile();
* const proof = await zkf.prove(publicInput, privateInput1, privateInput2);
* const isValid = await zkf.verify(proof, verificationKey);
* ```
*/
async verify(proof, verificationKey) {
return await proof.verify(verificationKey);
},
};
}
/**
* Encapsulates a {@link ZkFunction} proof together with its public input fields.
*
* Generated by {@link ZkFunction.prove}, it wraps the raw `Snarky.Proof`
* and the array of `Field` inputs used for verification.
*
* You can call `verify` on a `Proof` to check its validity against a
* {@link VerificationKey}.
*/
class KimchiProof {
constructor(value, publicInputFields) {
this.value = value;
this.publicInputFields = publicInputFields;
}
/**
* Verifies this proof using the provided verification key.
* @param verificationKey The key to verify against.
* @returns A promise that resolves to `true` if valid, otherwise `false`.
*/
async verify(verificationKey) {
await initializeBindings();
return prettifyStacktracePromise(withThreadPool(async () => Snarky.circuit.verify(MlFieldConstArray.to(this.publicInputFields), this.value, verificationKey.value)));
}
}
/**
* A verification key is used to verify a {@link Proof}.
*/
class KimchiVerificationKey {
constructor(value) {
this.value = value;
}
}
function mainFromCircuitData(config, privateInputs) {
return function main(publicInputFields) {
let id = snarkContext.enter({ inCheckedComputation: true });
try {
const publicInput = provablePure(config.publicInputType ?? undefined).fromFields(MlFieldArray.from(publicInputFields));
const privateInputs_ = config.privateInputTypes.map((typ, i) => Provable.witness(typ, () => (privateInputs ? privateInputs[i] : undefined)));
if (config.publicInputType !== undefined) {
config.main(publicInput, ...privateInputs_);
}
else {
config.main(...privateInputs_);
}
}
finally {
snarkContext.leave(id);
}
};
}
//# sourceMappingURL=zkfunction.js.map