UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

190 lines (172 loc) 6.04 kB
import { From } from '../../bindings/lib/provable-generic.js'; import { Bool } from '../provable/bool.js'; import { Provable } from '../provable/provable.js'; import { ProvableType } from '../provable/types/provable-intf.js'; import { InferProvable } from '../provable/types/struct.js'; import { mapObject, mapToObject, zip } from '../util/arrays.js'; import { Tuple } from '../util/types.js'; import { Proof } from './proof.js'; import { Undefined, Void } from './zkprogram.js'; export { Recursive }; function Recursive< PublicInputType extends Provable<any>, PublicOutputType extends Provable<any>, PrivateInputs extends { [Key in string]: Tuple<ProvableType>; }, >( zkprogram: { name: string; publicInputType: PublicInputType; publicOutputType: PublicOutputType; privateInputTypes: PrivateInputs; rawMethods: { [Key in keyof PrivateInputs]: ( ...args: any ) => Promise<{ publicOutput: InferProvable<PublicOutputType> }>; }; maxProofsVerified: () => Promise<0 | 1 | 2>; } & { [Key in keyof PrivateInputs]: (...args: any) => Promise<{ proof: Proof<InferProvable<PublicInputType>, InferProvable<PublicOutputType>>; }>; } ): { [Key in keyof PrivateInputs]: RecursiveProver< InferProvable<PublicInputType>, PublicInputType, InferProvable<PublicOutputType>, PrivateInputs[Key] > & { if: ConditionalRecursiveProver< InferProvable<PublicInputType>, PublicInputType, InferProvable<PublicOutputType>, PrivateInputs[Key] >; }; } { type PublicInput = InferProvable<PublicInputType>; type PublicOutput = InferProvable<PublicOutputType>; type MethodKey = keyof PrivateInputs; let { publicInputType, publicOutputType, privateInputTypes: privateInputs, rawMethods: methods, } = zkprogram; let hasPublicInput = publicInputType !== Undefined && publicInputType !== Void; class SelfProof extends Proof<PublicInput, PublicOutput> { static publicInputType = publicInputType; static publicOutputType = publicOutputType; static tag = () => zkprogram; } let methodKeys: MethodKey[] = Object.keys(methods); let regularRecursiveProvers = mapToObject(methodKeys, (key) => { return async function proveRecursively_( conditionAndConfig: Bool | { condition: Bool; domainLog2?: number }, publicInput: PublicInput, ...args: TupleFrom<PrivateInputs[MethodKey]> ): Promise<PublicOutput> { let condition = conditionAndConfig instanceof Bool ? conditionAndConfig : conditionAndConfig.condition; // create the base proof in a witness block let proof = await Provable.witnessAsync(SelfProof, async () => { // move method args to constants let constInput = Provable.toConstant<PublicInput>( publicInputType, publicInputType.fromValue(publicInput) ); let constArgs = zip(args, privateInputs[key]).map(([arg, type]) => Provable.toConstant(type, ProvableType.get(type).fromValue(arg)) ); if (!condition.toBoolean()) { let publicOutput: PublicOutput = ProvableType.synthesize(publicOutputType); let maxProofsVerified = await zkprogram.maxProofsVerified(); return SelfProof.dummy( publicInput, publicOutput, maxProofsVerified, conditionAndConfig instanceof Bool ? undefined : conditionAndConfig.domainLog2 ); } let prover = zkprogram[key]; if (hasPublicInput) { let { proof } = await prover(constInput, ...constArgs); return proof; } else { let { proof } = await prover(...constArgs); return proof; } }); // assert that the witnessed proof has the correct public input (which will be used by Pickles as part of verification) if (hasPublicInput) { Provable.assertEqual(publicInputType, proof.publicInput, publicInput); } // declare and verify the proof, and return its public output proof.declare(); proof.verifyIf(condition); return proof.publicOutput; }; }); return mapObject( regularRecursiveProvers, ( prover ): RecursiveProver<PublicInput, PublicInputType, PublicOutput, PrivateInputs[MethodKey]> & { if: ConditionalRecursiveProver< PublicInput, PublicInputType, PublicOutput, PrivateInputs[MethodKey] >; } => { if (!hasPublicInput) { return Object.assign( ((...args: any) => prover(new Bool(true), undefined as any, ...args)) as any, { if: (condition: Bool | { condition: Bool; domainLog2?: number }, ...args: any) => prover(condition, undefined as any, ...args), } ); } else { return Object.assign( ((pi: PublicInput, ...args: any) => prover(new Bool(true), pi, ...args)) as any, { if: ( condition: Bool | { condition: Bool; domainLog2?: number }, pi: PublicInput, ...args: any ) => prover(condition, pi, ...args), } ); } } ); } type RecursiveProver< PublicInput, PublicInputType, PublicOutput, Args extends Tuple<ProvableType>, > = PublicInput extends undefined ? (...args: TupleFrom<Args>) => Promise<PublicOutput> : (publicInput: From<PublicInputType>, ...args: TupleFrom<Args>) => Promise<PublicOutput>; type ConditionalRecursiveProver< PublicInput, PublicInputType, PublicOutput, Args extends Tuple<ProvableType>, > = PublicInput extends undefined ? ( condition: Bool | { condition: Bool; domainLog2?: number }, ...args: TupleFrom<Args> ) => Promise<PublicOutput> : ( condition: Bool | { condition: Bool; domainLog2?: number }, publicInput: From<PublicInputType>, ...args: TupleFrom<Args> ) => Promise<PublicOutput>; type TupleFrom<T> = { [I in keyof T]: From<T[I]>; };