From 8cea470ce9545b01645a415fac1ef2f98342f704 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 26 Aug 2024 11:22:57 +0200 Subject: [PATCH 01/11] optional proving for zkprogram --- src/lib/proof-system/zkprogram.ts | 88 +++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index fe497a7a65..fa4bec7b21 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -561,7 +561,11 @@ function ZkProgram< } ): { name: string; - compile: (options?: { cache?: Cache; forceRecompile?: boolean }) => Promise<{ + compile: (options?: { + cache?: Cache; + forceRecompile?: boolean; + proofsEnabled?: boolean; + }) => Promise<{ verificationKey: { data: string; hash: Field }; }>; verify: ( @@ -597,6 +601,8 @@ function ZkProgram< Types[I] >; } { + let proofsEnabled_ = true; + let methods = config.methods; let publicInputType: ProvablePure = ProvableType.get( config.publicInput ?? Undefined @@ -656,22 +662,32 @@ function ZkProgram< async function compile({ cache = Cache.FileSystemDefault, forceRecompile = false, + proofsEnabled = true, } = {}) { + proofsEnabled_ = proofsEnabled; let methodsMeta = await analyzeMethods(); let gates = methodKeys.map((k) => methodsMeta[k].gates); - let { provers, verify, verificationKey } = await compileProgram({ - publicInputType, - publicOutputType, - methodIntfs, - methods: methodFunctions, - gates, - proofSystemTag: selfTag, - cache, - forceRecompile, - overrideWrapDomain: config.overrideWrapDomain, - }); - compileOutput = { provers, verify }; - return { verificationKey }; + + if (proofsEnabled_) { + let { provers, verify, verificationKey } = await compileProgram({ + publicInputType, + publicOutputType, + methodIntfs, + methods: methodFunctions, + gates, + proofSystemTag: selfTag, + cache, + forceRecompile, + overrideWrapDomain: config.overrideWrapDomain, + }); + + compileOutput = { provers, verify }; + return { verificationKey }; + } else { + return { + verificationKey: VerificationKey.empty(), + }; + } } function toProver( @@ -682,6 +698,12 @@ function ZkProgram< publicInput: PublicInput, ...args: TupleToInstances ): Promise> { + class ProgramProof extends Proof { + static publicInputType = publicInputType; + static publicOutputType = publicOutputType; + static tag = () => selfTag; + } + let picklesProver = compileOutput?.provers?.[i]; if (picklesProver === undefined) { throw Error( @@ -703,11 +725,7 @@ function ZkProgram< } let [publicOutputFields, proof] = MlPair.from(result); let publicOutput = fromFieldConsts(publicOutputType, publicOutputFields); - class ProgramProof extends Proof { - static publicInputType = publicInputType; - static publicOutputType = publicOutputType; - static tag = () => selfTag; - } + return new ProgramProof({ publicInput, publicOutput, @@ -715,18 +733,46 @@ function ZkProgram< maxProofsVerified, }); } + + async function dummyProve_( + publicInput: PublicInput, + ...args: TupleToInstances + ): Promise> { + class ProgramProof extends Proof { + static publicInputType = publicInputType; + static publicOutputType = publicOutputType; + static tag = () => selfTag; + } + + let publicInputFields = toFieldConsts(publicInputType, publicInput); + let previousProofs = MlArray.to( + getPreviousProofsForProver(args, methodIntfs[i]) + ); + + let publicOutput = await (methods[key].method as any)( + publicInputFields, + previousProofs + ); + + return ProgramProof.dummy(publicInput, publicOutput, maxProofsVerified); + } + let prove: Prover; if ( (publicInputType as any) === Undefined || (publicInputType as any) === Void ) { prove = ((...args: TupleToInstances) => - (prove_ as any)(undefined, ...args)) as any; + (proofsEnabled_ ? prove_ : (dummyProve_ as any))( + undefined, + ...(args as any) + )) as any; } else { - prove = prove_ as any; + prove = (proofsEnabled_ ? prove_ : dummyProve_) as any; } return [key, prove]; } + let provers = Object.fromEntries(methodKeys.map(toProver)) as { [I in keyof Types]: Prover; }; From 74f136ad90b60de7c172b9aeb09b5a0eebb97ea6 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 26 Aug 2024 11:27:55 +0200 Subject: [PATCH 02/11] simplify --- src/lib/proof-system/zkprogram.ts | 43 +++++++++++-------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index fa4bec7b21..5b4ea31a4f 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -704,6 +704,19 @@ function ZkProgram< static tag = () => selfTag; } + if (!proofsEnabled_) { + let previousProofs = MlArray.to( + getPreviousProofsForProver(args, methodIntfs[i]) + ); + + let publicOutput = await (methods[key].method as any)( + publicInput, + previousProofs + ); + + return ProgramProof.dummy(publicInput, publicOutput, maxProofsVerified); + } + let picklesProver = compileOutput?.provers?.[i]; if (picklesProver === undefined) { throw Error( @@ -734,41 +747,15 @@ function ZkProgram< }); } - async function dummyProve_( - publicInput: PublicInput, - ...args: TupleToInstances - ): Promise> { - class ProgramProof extends Proof { - static publicInputType = publicInputType; - static publicOutputType = publicOutputType; - static tag = () => selfTag; - } - - let publicInputFields = toFieldConsts(publicInputType, publicInput); - let previousProofs = MlArray.to( - getPreviousProofsForProver(args, methodIntfs[i]) - ); - - let publicOutput = await (methods[key].method as any)( - publicInputFields, - previousProofs - ); - - return ProgramProof.dummy(publicInput, publicOutput, maxProofsVerified); - } - let prove: Prover; if ( (publicInputType as any) === Undefined || (publicInputType as any) === Void ) { prove = ((...args: TupleToInstances) => - (proofsEnabled_ ? prove_ : (dummyProve_ as any))( - undefined, - ...(args as any) - )) as any; + (prove_ as any)(undefined, ...(args as any))) as any; } else { - prove = (proofsEnabled_ ? prove_ : dummyProve_) as any; + prove = prove_ as any; } return [key, prove]; } From 2e8d2d0d9b1e8529616f76524c0daed0f40e9579 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 26 Aug 2024 11:34:34 +0200 Subject: [PATCH 03/11] mock verification --- src/lib/proof-system/zkprogram.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 5b4ea31a4f..dc8ba22c8b 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -601,7 +601,7 @@ function ZkProgram< Types[I] >; } { - let proofsEnabled_ = true; + let doProving = true; let methods = config.methods; let publicInputType: ProvablePure = ProvableType.get( @@ -664,11 +664,11 @@ function ZkProgram< forceRecompile = false, proofsEnabled = true, } = {}) { - proofsEnabled_ = proofsEnabled; + doProving = proofsEnabled; let methodsMeta = await analyzeMethods(); let gates = methodKeys.map((k) => methodsMeta[k].gates); - if (proofsEnabled_) { + if (doProving) { let { provers, verify, verificationKey } = await compileProgram({ publicInputType, publicOutputType, @@ -704,7 +704,7 @@ function ZkProgram< static tag = () => selfTag; } - if (!proofsEnabled_) { + if (!doProving) { let previousProofs = MlArray.to( getPreviousProofsForProver(args, methodIntfs[i]) ); @@ -765,6 +765,9 @@ function ZkProgram< }; function verify(proof: Proof) { + if (!doProving) { + return Promise.resolve(true); + } if (compileOutput?.verify === undefined) { throw Error( `Cannot verify proof, verification key not found. Try calling \`await program.compile()\` first.` From fec008a430c3778423f70f0b6f90b840c292d9c4 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 26 Aug 2024 11:40:18 +0200 Subject: [PATCH 04/11] add simple example --- src/examples/zkprogram/program-no-proving.ts | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/examples/zkprogram/program-no-proving.ts diff --git a/src/examples/zkprogram/program-no-proving.ts b/src/examples/zkprogram/program-no-proving.ts new file mode 100644 index 0000000000..c8fa8b4c4a --- /dev/null +++ b/src/examples/zkprogram/program-no-proving.ts @@ -0,0 +1,36 @@ +import { + SelfProof, + Field, + ZkProgram, + verify, + Proof, + JsonProof, + Provable, + Empty, + Cache, +} from 'o1js'; + +let MyProgram = ZkProgram({ + name: 'example-without-proving', + publicOutput: Field, + publicInput: Field, + methods: { + baseCase: { + privateInputs: [], + async method(publicInput: Field) { + return publicInput.add(4); + }, + }, + }, +}); + +console.log('program digest', await MyProgram.digest()); +let cs = await MyProgram.analyzeMethods(); +console.log(cs); +let { verificationKey } = await MyProgram.compile({ + proofsEnabled: false, +}); + +console.log('proving base case...'); +let proof = await MyProgram.baseCase(Field(2)); +proof.publicOutput.assertEquals(Field(2).add(4)); From 9b2951cebdd11042e8db4964ab7b498b3c3d35c1 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 26 Aug 2024 11:40:57 +0200 Subject: [PATCH 05/11] add comment --- src/examples/zkprogram/program-no-proving.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/examples/zkprogram/program-no-proving.ts b/src/examples/zkprogram/program-no-proving.ts index c8fa8b4c4a..2f17c97fe8 100644 --- a/src/examples/zkprogram/program-no-proving.ts +++ b/src/examples/zkprogram/program-no-proving.ts @@ -25,10 +25,12 @@ let MyProgram = ZkProgram({ }); console.log('program digest', await MyProgram.digest()); -let cs = await MyProgram.analyzeMethods(); -console.log(cs); + +// disable proving for iterating quicker +const proofsEnabled = false; + let { verificationKey } = await MyProgram.compile({ - proofsEnabled: false, + proofsEnabled, }); console.log('proving base case...'); From 5b7a825f899338ec1d354136e5aa4d1063f41b02 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 10:18:37 +0200 Subject: [PATCH 06/11] changelog and comment --- CHANGELOG.md | 1 + src/examples/zkprogram/program-no-proving.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5390d99522..dab8394ed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `SmartContract.emitEventIf()` to conditionally emit an event https://github.com/o1-labs/o1js/pull/1746 - Added `Encryption.encryptV2()` and `Encryption.decryptV2()` for an updated encryption algorithm that guarantees cipher text integrity. - Also added `Encryption.encryptBytes()` and `Encryption.decryptBytes()` using the same algorithm. +- New option `proofsEnabled` for `zkProgram` (default value: `true`), to quickly test circuit logic with proofs disabled https://github.com/o1-labs/o1js/pull/1805 ### Changed diff --git a/src/examples/zkprogram/program-no-proving.ts b/src/examples/zkprogram/program-no-proving.ts index 2f17c97fe8..0a61b85b2d 100644 --- a/src/examples/zkprogram/program-no-proving.ts +++ b/src/examples/zkprogram/program-no-proving.ts @@ -26,10 +26,10 @@ let MyProgram = ZkProgram({ console.log('program digest', await MyProgram.digest()); -// disable proving for iterating quicker +// disable proofs to accelerate iteration during development const proofsEnabled = false; -let { verificationKey } = await MyProgram.compile({ +await MyProgram.compile({ proofsEnabled, }); From a18e2b98a0cde39e848556a77b69c0a3e01096a5 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 10:59:28 +0200 Subject: [PATCH 07/11] add custom setters and getters --- CHANGELOG.md | 1 + src/examples/zkprogram/program-no-proving.ts | 18 +++++++++++++++--- src/lib/proof-system/zkprogram.ts | 15 +++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dab8394ed6..00ec5bd3b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Added `Encryption.encryptV2()` and `Encryption.decryptV2()` for an updated encryption algorithm that guarantees cipher text integrity. - Also added `Encryption.encryptBytes()` and `Encryption.decryptBytes()` using the same algorithm. - New option `proofsEnabled` for `zkProgram` (default value: `true`), to quickly test circuit logic with proofs disabled https://github.com/o1-labs/o1js/pull/1805 + - Additionally added `MyProgram.proofsEnabled` to get the internal value of `proofsEnabled` and `MyProgram.setProofsEnabled(proofsEnabled)` to set the value dynamically. ### Changed diff --git a/src/examples/zkprogram/program-no-proving.ts b/src/examples/zkprogram/program-no-proving.ts index 0a61b85b2d..bce5214787 100644 --- a/src/examples/zkprogram/program-no-proving.ts +++ b/src/examples/zkprogram/program-no-proving.ts @@ -26,13 +26,25 @@ let MyProgram = ZkProgram({ console.log('program digest', await MyProgram.digest()); -// disable proofs to accelerate iteration during development -const proofsEnabled = false; +// enable proofs to compile the program +const proofsEnabled = true; await MyProgram.compile({ proofsEnabled, }); -console.log('proving base case...'); +console.log('proofs enabled?', MyProgram.proofsEnabled); + +console.log('proving base case... (proofs enabled)'); +console.time('proving'); let proof = await MyProgram.baseCase(Field(2)); +console.timeEnd('proving'); +proof.publicOutput.assertEquals(Field(2).add(4)); + +console.log('disable proofs, generate dummy proof'); +MyProgram.setProofsEnabled(false); +console.log('proofs enabled?', MyProgram.proofsEnabled); +console.time('noProving'); +proof = await MyProgram.baseCase(Field(2)); +console.timeEnd('noProving'); proof.publicOutput.assertEquals(Field(2).add(4)); diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index dc8ba22c8b..e17f51cb95 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -594,6 +594,8 @@ function ZkProgram< Types[I] >['method']; }; + proofsEnabled: boolean; + setProofsEnabled(proofsEnabled: boolean): void; } & { [I in keyof Types]: Prover< InferProvableOrUndefined>, @@ -665,6 +667,7 @@ function ZkProgram< proofsEnabled = true, } = {}) { doProving = proofsEnabled; + let methodsMeta = await analyzeMethods(); let gates = methodKeys.map((k) => methodsMeta[k].gates); @@ -721,7 +724,7 @@ function ZkProgram< if (picklesProver === undefined) { throw Error( `Cannot prove execution of program.${key}(), no prover found. ` + - `Try calling \`await program.compile()\` first, this will cache provers in the background.` + `Try calling \`await program.compile()\` first, this will cache provers in the background.\nIf you compiled your zkProgram with proofs disabled (\`proofsEnabled = false\`), you have to compile it with proofs enabled first.` ); } let publicInputFields = toFieldConsts(publicInputType, publicInput); @@ -789,7 +792,15 @@ function ZkProgram< } return Object.assign( - selfTag, + { + ...selfTag, + get proofsEnabled() { + return doProving; + }, + setProofsEnabled(proofsEnabled: boolean) { + doProving = proofsEnabled; + }, + }, { compile, verify, From a28e1f9102402e67108b283cd097453a08509c26 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 11:28:34 +0200 Subject: [PATCH 08/11] fix failing test --- src/lib/proof-system/zkprogram.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index e17f51cb95..cc556b8cbe 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -613,7 +613,15 @@ function ZkProgram< config.publicOutput ?? Void ); - let selfTag = { name: config.name }; + let selfTag = { + name: config.name, + get proofsEnabled() { + return doProving; + }, + setProofsEnabled(proofsEnabled: boolean) { + doProving = proofsEnabled; + }, + }; type PublicInput = InferProvableOrUndefined< Get >; @@ -792,15 +800,8 @@ function ZkProgram< } return Object.assign( - { - ...selfTag, - get proofsEnabled() { - return doProving; - }, - setProofsEnabled(proofsEnabled: boolean) { - doProving = proofsEnabled; - }, - }, + selfTag, + { compile, verify, From 2ecbf417a58565e14f7f4691e2053586898eb6ef Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 11:32:19 +0200 Subject: [PATCH 09/11] simplify type --- src/lib/proof-system/zkprogram.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index cc556b8cbe..30f9d40c7b 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -763,8 +763,7 @@ function ZkProgram< (publicInputType as any) === Undefined || (publicInputType as any) === Void ) { - prove = ((...args: TupleToInstances) => - (prove_ as any)(undefined, ...(args as any))) as any; + prove = ((...args: any) => prove_(undefined as any, ...args)) as any; } else { prove = prove_ as any; } From 99bd508832dd6f659496934cf61ec2f929499f95 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 11:33:23 +0200 Subject: [PATCH 10/11] simplify --- src/lib/proof-system/zkprogram.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 30f9d40c7b..776a2d3e88 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -676,10 +676,10 @@ function ZkProgram< } = {}) { doProving = proofsEnabled; - let methodsMeta = await analyzeMethods(); - let gates = methodKeys.map((k) => methodsMeta[k].gates); - if (doProving) { + let methodsMeta = await analyzeMethods(); + let gates = methodKeys.map((k) => methodsMeta[k].gates); + let { provers, verify, verificationKey } = await compileProgram({ publicInputType, publicOutputType, From fc8f8d88af7f4d5af6202679309ee46d9472cc33 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 4 Sep 2024 11:45:10 +0200 Subject: [PATCH 11/11] define explicit property --- src/lib/proof-system/zkprogram.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/lib/proof-system/zkprogram.ts b/src/lib/proof-system/zkprogram.ts index 776a2d3e88..8c5f83da9b 100644 --- a/src/lib/proof-system/zkprogram.ts +++ b/src/lib/proof-system/zkprogram.ts @@ -615,12 +615,6 @@ function ZkProgram< let selfTag = { name: config.name, - get proofsEnabled() { - return doProving; - }, - setProofsEnabled(proofsEnabled: boolean) { - doProving = proofsEnabled; - }, }; type PublicInput = InferProvableOrUndefined< Get @@ -798,9 +792,8 @@ function ZkProgram< return hashConstant(digests).toBigInt().toString(16); } - return Object.assign( + const program = Object.assign( selfTag, - { compile, verify, @@ -818,9 +811,19 @@ function ZkProgram< rawMethods: Object.fromEntries( methodKeys.map((key) => [key, methods[key].method]) ) as any, + setProofsEnabled(proofsEnabled: boolean) { + doProving = proofsEnabled; + }, }, provers ); + + // Object.assign only shallow-copies, hence we cant use this getter and have to define it explicitly + Object.defineProperty(program, 'proofsEnabled', { + get: () => doProving, + }); + + return program as ZkProgram; } type ZkProgram<