From 5866a011f681cc591de8ad1a7a1abdf406d4dc8e Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 11:57:56 +0300 Subject: [PATCH 1/6] test: add recursive program to e2e testing --- src/build/e2e-tests-build-helper.js | 40 ++++++++++++++----- src/examples/zkprogram/program-with-input.ts | 2 +- .../html/on-chain-state-mgmt-zkapp-ui.html | 4 ++ .../on-chain-state-mgmt-zkapp-ui.js | 18 +++++++++ tests/on-chain-state-mgmt-zkapp-ui.spec.ts | 10 +++++ tests/pages/on-chain-state-mgmt-zkapp.ts | 11 +++++ 6 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/build/e2e-tests-build-helper.js b/src/build/e2e-tests-build-helper.js index 3709c514e8..d2b9adeca1 100644 --- a/src/build/e2e-tests-build-helper.js +++ b/src/build/e2e-tests-build-helper.js @@ -1,14 +1,32 @@ import replace from 'replace-in-file'; -const options = { - files: './dist/web/examples/zkapps/**/*.js', - from: /from 'o1js'/g, - to: "from '../../../index.js'", -}; - -try { - const results = await replace(options); - console.log('Replacement results:', results); -} catch (error) { - console.error('Error occurred:', error); +const replaceOptions = [ + { + files: './dist/web/examples/zkapps/**/*.js', + from: /from 'o1js'/g, + to: "from '../../../index.js'", + }, + { + files: './dist/web/examples/zkprogram/*.js', + from: /from 'o1js'/g, + to: "from '../../index.js'", + }, +]; + +async function performReplacement(options) { + try { + const results = await replace(options); + console.log(`Replacement results for ${options.files}:`, results); + } catch (error) { + console.error(`Error occurred while replacing in ${options.files}:`, error); + } +} + +async function main() { + for (const options of replaceOptions) { + await performReplacement(options); + } } + +main(); + diff --git a/src/examples/zkprogram/program-with-input.ts b/src/examples/zkprogram/program-with-input.ts index a6117771b3..a1a6a71cde 100644 --- a/src/examples/zkprogram/program-with-input.ts +++ b/src/examples/zkprogram/program-with-input.ts @@ -8,7 +8,7 @@ import { Provable, } from 'o1js'; -let MyProgram = ZkProgram({ +export let MyProgram = ZkProgram({ name: 'example-with-input', publicInput: Field, diff --git a/tests/artifacts/html/on-chain-state-mgmt-zkapp-ui.html b/tests/artifacts/html/on-chain-state-mgmt-zkapp-ui.html index b9bad32a17..980822b28c 100644 --- a/tests/artifacts/html/on-chain-state-mgmt-zkapp-ui.html +++ b/tests/artifacts/html/on-chain-state-mgmt-zkapp-ui.html @@ -25,6 +25,10 @@

UI for the On-Chain State Management zkApp


+

zkProgram Management

+
+ +

zkApp Management

diff --git a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js index bfeed49252..60b3fd340b 100644 --- a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js +++ b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js @@ -3,8 +3,10 @@ import { adminPrivateKey, HelloWorld, } from './examples/zkapps/hello-world/hello-world.js'; +import { MyProgram } from './examples/zkprogram/program-with-input.js' import { AccountUpdate, Field, Mina, verify } from './index.js'; +const compileButton = document.querySelector('#compileButton'); const deployButton = document.querySelector('#deployButton'); const updateButton = document.querySelector('#updateButton'); const clearEventsButton = document.querySelector('#clearEventsButton'); @@ -25,6 +27,22 @@ const contractAddress = Mina.TestPublicKey.random(); const contract = new HelloWorld(contractAddress); let verificationKey = null; +compileButton.addEventListener('click', async () => { + compileButton.disabled = true; + + logEvents('Compiling ZkProgram', eventsContainer); + + try { + await MyProgram.compile(); + logEvents('ZkProgram compiled successfully!', eventsContainer); + } catch (exception) { + logEvents(`Compilation failure: ${exception.message}`, eventsContainer); + console.log(exception); + } + + compileButton.disabled = false; +}); + deployButton.addEventListener('click', async () => { deployButton.disabled = true; diff --git a/tests/on-chain-state-mgmt-zkapp-ui.spec.ts b/tests/on-chain-state-mgmt-zkapp-ui.spec.ts index 7fba7fd26d..76a7c72e4b 100644 --- a/tests/on-chain-state-mgmt-zkapp-ui.spec.ts +++ b/tests/on-chain-state-mgmt-zkapp-ui.spec.ts @@ -8,6 +8,16 @@ test.describe('On-Chain State Management zkApp UI', () => { await onChainStateMgmtZkAppPage.checkO1jsInitialization(); }); + test('should compile zkProgram', async ({ + onChainStateMgmtZkAppPage, + }) => { + await onChainStateMgmtZkAppPage.goto(); + await onChainStateMgmtZkAppPage.checkO1jsInitialization(); + await onChainStateMgmtZkAppPage.compileZkProgram(); + await onChainStateMgmtZkAppPage.checkZkProgramCompilation(); + }); + + test('should fail to update account state since zkApp was not yet deployed', async ({ onChainStateMgmtZkAppPage, }) => { diff --git a/tests/pages/on-chain-state-mgmt-zkapp.ts b/tests/pages/on-chain-state-mgmt-zkapp.ts index bf1a8027d9..db36d011d2 100644 --- a/tests/pages/on-chain-state-mgmt-zkapp.ts +++ b/tests/pages/on-chain-state-mgmt-zkapp.ts @@ -2,6 +2,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; export class OnChainStateMgmtZkAppPage { readonly page: Page; + readonly compileButton: Locator; readonly deployButton: Locator; readonly updateButton: Locator; readonly clearEventsButton: Locator; @@ -11,6 +12,7 @@ export class OnChainStateMgmtZkAppPage { constructor(page: Page) { this.page = page; + this.compileButton = page.locator('button[id="compileButton"]'); this.deployButton = page.locator('button[id="deployButton"]'); this.updateButton = page.locator('button[id="updateButton"]'); this.clearEventsButton = page.locator('button[id="clearEventsButton"]'); @@ -23,6 +25,10 @@ export class OnChainStateMgmtZkAppPage { await this.page.goto('/on-chain-state-mgmt-zkapp-ui.html'); } + async compileZkProgram() { + await this.compileButton.click(); + } + async compileAndDeployZkApp() { await this.deployButton.click(); } @@ -40,6 +46,11 @@ export class OnChainStateMgmtZkAppPage { await expect(this.eventsContainer).toContainText('o1js initialized after'); } + async checkZkProgramCompilation() { + await expect(this.eventsContainer).toContainText('Compiling ZkProgram'); + await expect(this.eventsContainer).toContainText('ZkProgram compiled successfully!'); + } + async checkDeployedZkApp() { await expect(this.eventsContainer).toContainText('Deploying'); await expect(this.eventsContainer).toContainText('Initial state: 2'); From bc28e8ee1d0df2819311e45e4de5c5b5714ef80d Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 17:46:12 +0300 Subject: [PATCH 2/6] test: add comments to elaborate why we import MyProgram --- tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js index 60b3fd340b..6848305810 100644 --- a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js +++ b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js @@ -3,7 +3,10 @@ import { adminPrivateKey, HelloWorld, } from './examples/zkapps/hello-world/hello-world.js'; -import { MyProgram } from './examples/zkprogram/program-with-input.js' +// MyProgram to serve as a test case for recursive ZkProgram compilation. +// This helps to catch any regressions that might occur in the future. +// For more details, refer to: https://github.com/o1-labs/o1js/pull/1906 +import { MyProgram } from './examples/zkprogram/program-with-input.js'; import { AccountUpdate, Field, Mina, verify } from './index.js'; const compileButton = document.querySelector('#compileButton'); From db35a1ca49e98cb092da88167f2a6b150e0e29de Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 20:53:52 +0300 Subject: [PATCH 3/6] test: add recursive zkprogram --- src/examples/zkprogram/program-with-input.ts | 2 +- src/examples/zkprogram/recursive-program.ts | 23 +++++++++++++++++++ .../on-chain-state-mgmt-zkapp-ui.js | 7 ++---- 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/examples/zkprogram/recursive-program.ts diff --git a/src/examples/zkprogram/program-with-input.ts b/src/examples/zkprogram/program-with-input.ts index a1a6a71cde..a6117771b3 100644 --- a/src/examples/zkprogram/program-with-input.ts +++ b/src/examples/zkprogram/program-with-input.ts @@ -8,7 +8,7 @@ import { Provable, } from 'o1js'; -export let MyProgram = ZkProgram({ +let MyProgram = ZkProgram({ name: 'example-with-input', publicInput: Field, diff --git a/src/examples/zkprogram/recursive-program.ts b/src/examples/zkprogram/recursive-program.ts new file mode 100644 index 0000000000..b6b6f3b61b --- /dev/null +++ b/src/examples/zkprogram/recursive-program.ts @@ -0,0 +1,23 @@ +import { SelfProof, Field, ZkProgram } from 'o1js'; + +export const RecusiveProgram = ZkProgram({ + name: 'recursive-program', + publicInput: Field, + + methods: { + baseCase: { + privateInputs: [], + async method(input: Field) { + input.assertEquals(Field(0)); + }, + }, + + inductiveCase: { + privateInputs: [SelfProof], + async method(input: Field, earlierProof: SelfProof) { + earlierProof.verify(); + earlierProof.publicInput.add(1).assertEquals(input); + }, + }, + }, +}); diff --git a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js index 6848305810..86392c0d0c 100644 --- a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js +++ b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js @@ -3,10 +3,7 @@ import { adminPrivateKey, HelloWorld, } from './examples/zkapps/hello-world/hello-world.js'; -// MyProgram to serve as a test case for recursive ZkProgram compilation. -// This helps to catch any regressions that might occur in the future. -// For more details, refer to: https://github.com/o1-labs/o1js/pull/1906 -import { MyProgram } from './examples/zkprogram/program-with-input.js'; +import { RecursiveProgram } from './examples/zkprogram/recursive-program.js'; import { AccountUpdate, Field, Mina, verify } from './index.js'; const compileButton = document.querySelector('#compileButton'); @@ -36,7 +33,7 @@ compileButton.addEventListener('click', async () => { logEvents('Compiling ZkProgram', eventsContainer); try { - await MyProgram.compile(); + await RecursiveProgram.compile(); logEvents('ZkProgram compiled successfully!', eventsContainer); } catch (exception) { logEvents(`Compilation failure: ${exception.message}`, eventsContainer); From e54cb3e01a26e395ab277532ffd5bae323eba12b Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 21:39:02 +0300 Subject: [PATCH 4/6] test: format helper --- src/build/e2e-tests-build-helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/build/e2e-tests-build-helper.js b/src/build/e2e-tests-build-helper.js index d2b9adeca1..f21d3f0fff 100644 --- a/src/build/e2e-tests-build-helper.js +++ b/src/build/e2e-tests-build-helper.js @@ -29,4 +29,3 @@ async function main() { } main(); - From bd529b8e14f0d30c659628db3690daf5eb5e805a Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 23:01:47 +0300 Subject: [PATCH 5/6] test: fix typo --- src/examples/zkprogram/recursive-program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkprogram/recursive-program.ts b/src/examples/zkprogram/recursive-program.ts index b6b6f3b61b..0308266cd8 100644 --- a/src/examples/zkprogram/recursive-program.ts +++ b/src/examples/zkprogram/recursive-program.ts @@ -1,6 +1,6 @@ import { SelfProof, Field, ZkProgram } from 'o1js'; -export const RecusiveProgram = ZkProgram({ +export const RecursiveProgram = ZkProgram({ name: 'recursive-program', publicInput: Field, From 6260b56ddf0491dee52755885c93907c19627c36 Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 20 Nov 2024 23:05:23 +0300 Subject: [PATCH 6/6] test: fix format to trigger gh actions --- tests/pages/on-chain-state-mgmt-zkapp.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/pages/on-chain-state-mgmt-zkapp.ts b/tests/pages/on-chain-state-mgmt-zkapp.ts index db36d011d2..d9e2e9b45b 100644 --- a/tests/pages/on-chain-state-mgmt-zkapp.ts +++ b/tests/pages/on-chain-state-mgmt-zkapp.ts @@ -48,7 +48,9 @@ export class OnChainStateMgmtZkAppPage { async checkZkProgramCompilation() { await expect(this.eventsContainer).toContainText('Compiling ZkProgram'); - await expect(this.eventsContainer).toContainText('ZkProgram compiled successfully!'); + await expect(this.eventsContainer).toContainText( + 'ZkProgram compiled successfully!' + ); } async checkDeployedZkApp() {