From 678188599c1d7217159081086631bc1b5af04b51 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:08:45 +0000 Subject: [PATCH 01/16] Add checks for STProver --- packages/protocol/src/state/State.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 36400d8c1..450ad98f0 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -135,11 +135,13 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - const stateTransition = StateTransition.from(this.path, option); + if (!Provable.inCheckedComputation().valueOf()) { + const stateTransition = StateTransition.from(this.path, option); - container - .resolve(RuntimeMethodExecutionContext) - .addStateTransition(stateTransition); + container + .resolve(RuntimeMethodExecutionContext) + .addStateTransition(stateTransition); + } return option; } @@ -168,6 +170,10 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); + if (Provable.inCheckedComputation().valueOf()) { + throw new Error("Cannot set state inside of provable block."); + } + container .resolve(RuntimeMethodExecutionContext) .addStateTransition(stateTransition); From 2cf21eb691b3f31fcec61771ffd4ca431739771e Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:18:33 +0000 Subject: [PATCH 02/16] Remove value of --- packages/protocol/src/state/State.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 450ad98f0..e1187f876 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -135,7 +135,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - if (!Provable.inCheckedComputation().valueOf()) { + if (!Provable.inCheckedComputation()) { const stateTransition = StateTransition.from(this.path, option); container @@ -170,7 +170,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - if (Provable.inCheckedComputation().valueOf()) { + if (Provable.inCheckedComputation()) { throw new Error("Cannot set state inside of provable block."); } From 4448d4adb0f4692c83750642b73f7e80e8b03596 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:53:54 +0000 Subject: [PATCH 03/16] Reverse conditional statement --- packages/protocol/src/state/State.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index e1187f876..18f61a8c7 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -135,7 +135,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - if (!Provable.inCheckedComputation()) { + if (Provable.inCheckedComputation()) { const stateTransition = StateTransition.from(this.path, option); container @@ -170,7 +170,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - if (Provable.inCheckedComputation()) { + if (!Provable.inCheckedComputation()) { throw new Error("Cannot set state inside of provable block."); } From b98e18621774c97b57c4346cd40ff92f2026f6c3 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:47:32 +0000 Subject: [PATCH 04/16] Add in tests. --- packages/sdk/test/stprover-emit-sts.test.ts | 77 +++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 packages/sdk/test/stprover-emit-sts.test.ts diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts new file mode 100644 index 000000000..c0af7b68a --- /dev/null +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -0,0 +1,77 @@ +import "reflect-metadata"; + +import { UInt64 } from "@proto-kit/library"; +import { runtimeMethod, runtimeModule, RuntimeModule } from "@proto-kit/module"; +import { PrivateKey, Provable } from "o1js"; +import { State, state } from "@proto-kit/protocol"; + +import { TestingAppChain } from "../src"; + +@runtimeModule() +class StateTester extends RuntimeModule { + @state() public state1 = State.from(UInt64); + + @runtimeMethod() + public async setFail() { + Provable.asProver(async () => { + await this.state1.set(UInt64.from(10)); + }); + } + + @runtimeMethod() + public async setPass() { + await this.state1.set(UInt64.from(10)); + } +} + +describe("StateTransition", () => { + const senderKey = PrivateKey.random(); + + const appChain = TestingAppChain.fromRuntime({ + StateTester, + }); + + beforeAll(async () => { + appChain.configurePartial({ + Runtime: { + StateTester: {}, + Balances: {}, + }, + + Protocol: { + ...appChain.config.Protocol!, + }, + }); + + await appChain.start(); + appChain.setSigner(senderKey); + }); + + it("should fails outside provable code", async () => { + const stateTester = appChain.runtime.resolve("StateTester"); + const tx1 = await appChain.transaction( + senderKey.toPublicKey(), + async () => { + await stateTester.setPass(); + } + ); + + await tx1.sign(); + await tx1.send(); + + await appChain.produceBlock(); + const tx2 = await appChain.transaction( + senderKey.toPublicKey(), + async () => { + await stateTester.setFail(); + } + ); + + await tx2.sign(); + await tx2.send(); + + await expect(() => appChain.produceBlock()).rejects.toThrow( + new Error("Cannot set state inside of provable block.") + ); + }); +}); From a164b529748c0304d9aceed7c89454c409461134 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:50:13 +0000 Subject: [PATCH 05/16] Spacing in tests --- packages/sdk/test/stprover-emit-sts.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index c0af7b68a..8961e5580 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -55,21 +55,18 @@ describe("StateTransition", () => { await stateTester.setPass(); } ); - await tx1.sign(); await tx1.send(); - await appChain.produceBlock(); + const tx2 = await appChain.transaction( senderKey.toPublicKey(), async () => { await stateTester.setFail(); } ); - await tx2.sign(); await tx2.send(); - await expect(() => appChain.produceBlock()).rejects.toThrow( new Error("Cannot set state inside of provable block.") ); From b96a5e79cb7ab389528790b400fd859266487f5a Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:16:54 +0000 Subject: [PATCH 06/16] Change conditionals --- packages/protocol/src/state/State.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 18f61a8c7..111350470 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -135,7 +135,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - if (Provable.inCheckedComputation()) { + if (Provable.inProver()) { const stateTransition = StateTransition.from(this.path, option); container @@ -170,7 +170,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - if (!Provable.inCheckedComputation()) { + if (Provable.inProver()) { throw new Error("Cannot set state inside of provable block."); } From 92938e19b0d2cd16bd04daf512ab7b2902b786a2 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:46:18 +0000 Subject: [PATCH 07/16] Change conditionals --- packages/protocol/src/state/State.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 111350470..e53f7a0ba 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -135,7 +135,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - if (Provable.inProver()) { + if (!(Provable.inProver() || !Provable.inCheckedComputation())) { const stateTransition = StateTransition.from(this.path, option); container @@ -170,7 +170,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - if (Provable.inProver()) { + if (Provable.inProver() && !Provable.inCheckedComputation()) { throw new Error("Cannot set state inside of provable block."); } From 41193b26bf9145763d3a53e13b739da6c5800c18 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 28 Feb 2025 17:12:02 +0100 Subject: [PATCH 08/16] Created IsInWitnessBlockContext to track witness blocks --- packages/protocol/src/state/State.ts | 26 ++++++++++++++++++--- packages/sdk/test/stprover-emit-sts.test.ts | 5 ++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index e53f7a0ba..e3b0b3222 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -1,6 +1,6 @@ import { Mixin } from "ts-mixer"; import { Bool, Field, Provable, type FlexibleProvablePure, Struct } from "o1js"; -import { container } from "tsyringe"; +import { container, singleton } from "tsyringe"; import { dummyValue } from "@proto-kit/common"; import { Path } from "../model/Path"; @@ -36,6 +36,22 @@ export class WithStateServiceProvider { } } +@singleton() +export class IsInWitnessBlockContext { + public isInWitnessBlock: number = 0; +} + +// TODO Same for Provable.witness() & Provable.witnessFields() + +const originalWitnessAsync = Provable.witnessAsync; +Provable.witnessAsync = async (e, f) => { + const context = container.resolve(IsInWitnessBlockContext); + context.isInWitnessBlock += 1; + const ret = await originalWitnessAsync(e, f); + context.isInWitnessBlock -= 1; + return ret; +}; + /** * Utilities for runtime module state, such as get/set */ @@ -135,7 +151,9 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - if (!(Provable.inProver() || !Provable.inCheckedComputation())) { + const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); + + if (isInWitnessBlock === 0) { const stateTransition = StateTransition.from(this.path, option); container @@ -170,7 +188,9 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - if (Provable.inProver() && !Provable.inCheckedComputation()) { + const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); + + if (isInWitnessBlock > 0) { throw new Error("Cannot set state inside of provable block."); } diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index 8961e5580..e2f4fa087 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -2,7 +2,7 @@ import "reflect-metadata"; import { UInt64 } from "@proto-kit/library"; import { runtimeMethod, runtimeModule, RuntimeModule } from "@proto-kit/module"; -import { PrivateKey, Provable } from "o1js"; +import { Field, PrivateKey, Provable } from "o1js"; import { State, state } from "@proto-kit/protocol"; import { TestingAppChain } from "../src"; @@ -13,8 +13,9 @@ class StateTester extends RuntimeModule { @runtimeMethod() public async setFail() { - Provable.asProver(async () => { + await Provable.witnessAsync(Field, async () => { await this.state1.set(UInt64.from(10)); + return Field(0); }); } From a11181bada9ff517bf073d8ab61a02f5feb4b49d Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Fri, 28 Feb 2025 16:53:33 +0000 Subject: [PATCH 09/16] Start on tests --- packages/protocol/src/state/State.ts | 38 ++++++++++++---- packages/sdk/test/stprover-emit-sts.test.ts | 48 ++++++++++++++++----- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index e3b0b3222..872e4cdce 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -41,17 +41,39 @@ export class IsInWitnessBlockContext { public isInWitnessBlock: number = 0; } -// TODO Same for Provable.witness() & Provable.witnessFields() +const rewriteAsyncWitnessFunction = ( + originalFuncDef: (arg0: any, arg1: any) => any +) => { + return async (e: any, f: any) => { + const context = container.resolve(IsInWitnessBlockContext); + context.isInWitnessBlock += 1; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const ret = await originalFuncDef(e, f); + context.isInWitnessBlock -= 1; + return ret; + }; +}; -const originalWitnessAsync = Provable.witnessAsync; -Provable.witnessAsync = async (e, f) => { - const context = container.resolve(IsInWitnessBlockContext); - context.isInWitnessBlock += 1; - const ret = await originalWitnessAsync(e, f); - context.isInWitnessBlock -= 1; - return ret; +const rewriteWitnessFunction = (originalFuncDef: any) => { + return (e: any, f: any) => { + const context = container.resolve(IsInWitnessBlockContext); + context.isInWitnessBlock += 1; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const ret = originalFuncDef(e, f); + context.isInWitnessBlock -= 1; + return ret; + }; }; +const originalWitnessAsync = Provable.witnessAsync; +Provable.witnessAsync = rewriteAsyncWitnessFunction(originalWitnessAsync); + +const originalWitness = Provable.witness; +Provable.witness = rewriteWitnessFunction(originalWitness); + +const originalWitnessFields = Provable.witnessFields; +Provable.witnessFields = rewriteWitnessFunction(originalWitnessFields); + /** * Utilities for runtime module state, such as get/set */ diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index e2f4fa087..0af5b744e 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -23,6 +23,19 @@ class StateTester extends RuntimeModule { public async setPass() { await this.state1.set(UInt64.from(10)); } + + @runtimeMethod() + public async getPass() { + await this.state1.get(); + } + + @runtimeMethod() + public async getFail() { + await Provable.witnessAsync(Field, async () => { + const stateReturned = await this.state1.get(); + return Field.from(stateReturned.value.toBigInt()); + }); + } } describe("StateTransition", () => { @@ -48,7 +61,7 @@ describe("StateTransition", () => { appChain.setSigner(senderKey); }); - it("should fails outside provable code", async () => { + it("should fails outside provable code for set", async () => { const stateTester = appChain.runtime.resolve("StateTester"); const tx1 = await appChain.transaction( senderKey.toPublicKey(), @@ -60,16 +73,29 @@ describe("StateTransition", () => { await tx1.send(); await appChain.produceBlock(); - const tx2 = await appChain.transaction( - senderKey.toPublicKey(), - async () => { + await expect(() => + appChain.transaction(senderKey.toPublicKey(), async () => { await stateTester.setFail(); - } - ); - await tx2.sign(); - await tx2.send(); - await expect(() => appChain.produceBlock()).rejects.toThrow( - new Error("Cannot set state inside of provable block.") - ); + }) + ).rejects.toThrow(new Error("Cannot set state inside of provable block.")); + }); + + it("should emit no sts for get", async () => { + // const stateTester = appChain.runtime.resolve("StateTester"); + // const tx1 = await appChain.transaction( + // senderKey.toPublicKey(), + // async () => { + // await stateTester.setPass(); + // } + // ); + // await tx1.sign(); + // await tx1.send(); + // await appChain.produceBlock(); + // + // await expect(() => + // appChain.transaction(senderKey.toPublicKey(), async () => { + // await stateTester.setFail(); + // }) + // ).rejects.toThrow(new Error("Cannot set state inside of provable block.")); }); }); From 8605ba739e3d6a7c733b883e4a3d1836bbfc6ea3 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Mon, 3 Mar 2025 12:09:27 +0000 Subject: [PATCH 10/16] Rename functions and add tests --- packages/protocol/src/state/State.ts | 10 +-- packages/sdk/test/stprover-emit-sts.test.ts | 67 ++++++++++++++------- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 872e4cdce..b8d4a3499 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -41,7 +41,7 @@ export class IsInWitnessBlockContext { public isInWitnessBlock: number = 0; } -const rewriteAsyncWitnessFunction = ( +const newAsyncWitnessFunction = ( originalFuncDef: (arg0: any, arg1: any) => any ) => { return async (e: any, f: any) => { @@ -54,7 +54,7 @@ const rewriteAsyncWitnessFunction = ( }; }; -const rewriteWitnessFunction = (originalFuncDef: any) => { +const newWitnessFunction = (originalFuncDef: any) => { return (e: any, f: any) => { const context = container.resolve(IsInWitnessBlockContext); context.isInWitnessBlock += 1; @@ -66,13 +66,13 @@ const rewriteWitnessFunction = (originalFuncDef: any) => { }; const originalWitnessAsync = Provable.witnessAsync; -Provable.witnessAsync = rewriteAsyncWitnessFunction(originalWitnessAsync); +Provable.witnessAsync = newAsyncWitnessFunction(originalWitnessAsync); const originalWitness = Provable.witness; -Provable.witness = rewriteWitnessFunction(originalWitness); +Provable.witness = newWitnessFunction(originalWitness); const originalWitnessFields = Provable.witnessFields; -Provable.witnessFields = rewriteWitnessFunction(originalWitnessFields); +Provable.witnessFields = newWitnessFunction(originalWitnessFields); /** * Utilities for runtime module state, such as get/set diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index 0af5b744e..a5c8cb35f 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -25,12 +25,12 @@ class StateTester extends RuntimeModule { } @runtimeMethod() - public async getPass() { + public async getSTs() { await this.state1.get(); } @runtimeMethod() - public async getFail() { + public async getNoSTs() { await Provable.witnessAsync(Field, async () => { const stateReturned = await this.state1.get(); return Field.from(stateReturned.value.toBigInt()); @@ -45,7 +45,7 @@ describe("StateTransition", () => { StateTester, }); - beforeAll(async () => { + beforeEach(async () => { appChain.configurePartial({ Runtime: { StateTester: {}, @@ -61,7 +61,47 @@ describe("StateTransition", () => { appChain.setSigner(senderKey); }); - it("should fails outside provable code for set", async () => { + it("should emit no sts for get", async () => { + const stateTester = appChain.runtime.resolve("StateTester"); + + // We set the state so when we fetch it it won't error. + const tx0 = await appChain.transaction( + senderKey.toPublicKey(), + async () => { + await stateTester.setPass(); + } + ); + await tx0.sign(); + await tx0.send(); + await appChain.produceBlock(); + + const tx1 = await appChain.transaction( + senderKey.toPublicKey(), + async () => { + await stateTester.getSTs(); + } + ); + await tx1.sign(); + await tx1.send(); + const block1 = await appChain.produceBlock(); + const block1Transactions = block1!.transactions[0].stateTransitions; + + expect(block1Transactions.length).not.toBe(0); + + const tx2 = await appChain.transaction( + senderKey.toPublicKey(), + async () => { + await stateTester.getNoSTs(); + } + ); + await tx2.sign(); + await tx2.send(); + const block2 = await appChain.produceBlock(); + const block2Transaction = block2!.transactions[0].stateTransitions; + expect(block2Transaction.length).toBe(0); + }); + + it("should fail outside provable code for set", async () => { const stateTester = appChain.runtime.resolve("StateTester"); const tx1 = await appChain.transaction( senderKey.toPublicKey(), @@ -79,23 +119,4 @@ describe("StateTransition", () => { }) ).rejects.toThrow(new Error("Cannot set state inside of provable block.")); }); - - it("should emit no sts for get", async () => { - // const stateTester = appChain.runtime.resolve("StateTester"); - // const tx1 = await appChain.transaction( - // senderKey.toPublicKey(), - // async () => { - // await stateTester.setPass(); - // } - // ); - // await tx1.sign(); - // await tx1.send(); - // await appChain.produceBlock(); - // - // await expect(() => - // appChain.transaction(senderKey.toPublicKey(), async () => { - // await stateTester.setFail(); - // }) - // ).rejects.toThrow(new Error("Cannot set state inside of provable block.")); - }); }); From 6321bd660e1adf0fb2577dc718436f738f0216de Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:27:22 +0000 Subject: [PATCH 11/16] Fix test --- packages/sdk/test/stprover-emit-sts.test.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index a5c8cb35f..b50f0de41 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -3,7 +3,12 @@ import "reflect-metadata"; import { UInt64 } from "@proto-kit/library"; import { runtimeMethod, runtimeModule, RuntimeModule } from "@proto-kit/module"; import { Field, PrivateKey, Provable } from "o1js"; -import { State, state } from "@proto-kit/protocol"; +import { + RuntimeMethodExecutionContext, + State, + state, +} from "@proto-kit/protocol"; +import { container } from "tsyringe"; import { TestingAppChain } from "../src"; @@ -63,6 +68,7 @@ describe("StateTransition", () => { it("should emit no sts for get", async () => { const stateTester = appChain.runtime.resolve("StateTester"); + const context = container.resolve(RuntimeMethodExecutionContext); // We set the state so when we fetch it it won't error. const tx0 = await appChain.transaction( @@ -83,10 +89,9 @@ describe("StateTransition", () => { ); await tx1.sign(); await tx1.send(); - const block1 = await appChain.produceBlock(); - const block1Transactions = block1!.transactions[0].stateTransitions; + const STs = context.current().result.stateTransitions; - expect(block1Transactions.length).not.toBe(0); + expect(STs.length).not.toBe(0); const tx2 = await appChain.transaction( senderKey.toPublicKey(), @@ -96,9 +101,8 @@ describe("StateTransition", () => { ); await tx2.sign(); await tx2.send(); - const block2 = await appChain.produceBlock(); - const block2Transaction = block2!.transactions[0].stateTransitions; - expect(block2Transaction.length).toBe(0); + const STs2 = context.current().result.stateTransitions; + expect(STs2.length).toBe(0); }); it("should fail outside provable code for set", async () => { From c238f8cac91dcf1b4a33846b1a0d0ef894f7e112 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:02:23 +0000 Subject: [PATCH 12/16] Code review changes --- packages/protocol/src/state/State.ts | 52 ++++++++++++++++++---------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index b8d4a3499..f2dc89c69 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -38,41 +38,55 @@ export class WithStateServiceProvider { @singleton() export class IsInWitnessBlockContext { - public isInWitnessBlock: number = 0; -} + public witnessBlockDepth: number = 0; -const newAsyncWitnessFunction = ( - originalFuncDef: (arg0: any, arg1: any) => any + public get isInWitnessBlock() { + return this.witnessBlockDepth > 0; + } +} +const asyncProxyWitnessFunction = ( + originalFuncDef: typeof Provable.witnessAsync ) => { - return async (e: any, f: any) => { + return async ([e, f]: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); - context.isInWitnessBlock += 1; + context.witnessBlockDepth += 1; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const ret = await originalFuncDef(e, f); - context.isInWitnessBlock -= 1; + context.witnessBlockDepth -= 1; return ret; }; }; -const newWitnessFunction = (originalFuncDef: any) => { - return (e: any, f: any) => { +const proxyWitnessFunction = (originalFuncDef: typeof Provable.witness) => { + return ([e, f]: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); - context.isInWitnessBlock += 1; + context.witnessBlockDepth += 1; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const ret = originalFuncDef(e, f); - context.isInWitnessBlock -= 1; + context.witnessBlockDepth -= 1; + return ret; + }; +}; + +const proxyWitnessFieldsFunction = ( + originalFuncDef: typeof Provable.witnessFields +) => { + return ([e, f]: Parameters) => { + const context = container.resolve(IsInWitnessBlockContext); + context.witnessBlockDepth += 1; + + const ret = originalFuncDef(e, f); + context.witnessBlockDepth -= 1; return ret; }; }; -const originalWitnessAsync = Provable.witnessAsync; -Provable.witnessAsync = newAsyncWitnessFunction(originalWitnessAsync); +Provable.witnessAsync = asyncProxyWitnessFunction(Provable.witnessAsync); -const originalWitness = Provable.witness; -Provable.witness = newWitnessFunction(originalWitness); +Provable.witness = proxyWitnessFunction(Provable.witness); -const originalWitnessFields = Provable.witnessFields; -Provable.witnessFields = newWitnessFunction(originalWitnessFields); +Provable.witnessFields = proxyWitnessFieldsFunction(Provable.witnessFields); /** * Utilities for runtime module state, such as get/set @@ -175,7 +189,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); - if (isInWitnessBlock === 0) { + if (!isInWitnessBlock) { const stateTransition = StateTransition.from(this.path, option); container @@ -212,7 +226,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); - if (isInWitnessBlock > 0) { + if (isInWitnessBlock) { throw new Error("Cannot set state inside of provable block."); } From 5ec4df5711212a727d22541707b1a376cf77a62e Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:31:28 +0000 Subject: [PATCH 13/16] Code review changes --- packages/protocol/src/state/State.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index f2dc89c69..6f2cd3a63 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -47,23 +47,23 @@ export class IsInWitnessBlockContext { const asyncProxyWitnessFunction = ( originalFuncDef: typeof Provable.witnessAsync ) => { - return async ([e, f]: Parameters) => { + return async (...args: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); context.witnessBlockDepth += 1; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const ret = await originalFuncDef(e, f); + const ret = await originalFuncDef(...args); context.witnessBlockDepth -= 1; return ret; }; }; const proxyWitnessFunction = (originalFuncDef: typeof Provable.witness) => { - return ([e, f]: Parameters) => { + return (...args: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); context.witnessBlockDepth += 1; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const ret = originalFuncDef(e, f); + const ret = originalFuncDef(...args); context.witnessBlockDepth -= 1; return ret; }; @@ -72,11 +72,11 @@ const proxyWitnessFunction = (originalFuncDef: typeof Provable.witness) => { const proxyWitnessFieldsFunction = ( originalFuncDef: typeof Provable.witnessFields ) => { - return ([e, f]: Parameters) => { + return (...args: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); context.witnessBlockDepth += 1; - const ret = originalFuncDef(e, f); + const ret = originalFuncDef(...args); context.witnessBlockDepth -= 1; return ret; }; From 6dd22d3df36464cc3e52c4d4c1ab818da5717d74 Mon Sep 17 00:00:00 2001 From: ejMina226 <118474890+ejMina226@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:40:23 +0000 Subject: [PATCH 14/16] Code review changes --- packages/protocol/src/state/State.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 6f2cd3a63..b6a35657f 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -69,13 +69,12 @@ const proxyWitnessFunction = (originalFuncDef: typeof Provable.witness) => { }; }; -const proxyWitnessFieldsFunction = ( - originalFuncDef: typeof Provable.witnessFields -) => { +const proxyWitnessFieldsFunction = (originalFuncDef: any) => { return (...args: Parameters) => { const context = container.resolve(IsInWitnessBlockContext); context.witnessBlockDepth += 1; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const ret = originalFuncDef(...args); context.witnessBlockDepth -= 1; return ret; From 2f4fcab8f1f703d9debe66706eb207707686a0d0 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Thu, 27 Nov 2025 18:55:03 +0900 Subject: [PATCH 15/16] Refactored witness block proxying --- packages/protocol/src/state/State.ts | 58 ++----------------- .../protocol/src/state/WitnessBlockContext.ts | 50 ++++++++++++++++ packages/sdk/test/stprover-emit-sts.test.ts | 4 ++ 3 files changed, 59 insertions(+), 53 deletions(-) create mode 100644 packages/protocol/src/state/WitnessBlockContext.ts diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index f4ac27b73..003618d6f 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -9,6 +9,7 @@ import { StateTransition } from "../model/StateTransition"; import { StateServiceProvider } from "./StateServiceProvider"; import { RuntimeMethodExecutionContext } from "./context/RuntimeMethodExecutionContext"; +import { WitnessBlockContext } from "./WitnessBlockContext"; export class WithPath { public path?: Field; @@ -36,57 +37,6 @@ export class WithStateServiceProvider { } } -@singleton() -export class IsInWitnessBlockContext { - public witnessBlockDepth: number = 0; - - public get isInWitnessBlock() { - return this.witnessBlockDepth > 0; - } -} -const asyncProxyWitnessFunction = ( - originalFuncDef: typeof Provable.witnessAsync -) => { - return async (...args: Parameters) => { - const context = container.resolve(IsInWitnessBlockContext); - context.witnessBlockDepth += 1; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const ret = await originalFuncDef(...args); - context.witnessBlockDepth -= 1; - return ret; - }; -}; - -const proxyWitnessFunction = (originalFuncDef: typeof Provable.witness) => { - return (...args: Parameters) => { - const context = container.resolve(IsInWitnessBlockContext); - context.witnessBlockDepth += 1; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const ret = originalFuncDef(...args); - context.witnessBlockDepth -= 1; - return ret; - }; -}; - -const proxyWitnessFieldsFunction = (originalFuncDef: any) => { - return (...args: Parameters) => { - const context = container.resolve(IsInWitnessBlockContext); - context.witnessBlockDepth += 1; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const ret = originalFuncDef(...args); - context.witnessBlockDepth -= 1; - return ret; - }; -}; - -Provable.witnessAsync = asyncProxyWitnessFunction(Provable.witnessAsync); - -Provable.witness = proxyWitnessFunction(Provable.witness); - -Provable.witnessFields = proxyWitnessFieldsFunction(Provable.witnessFields); - /** * Utilities for runtime module state, such as get/set */ @@ -188,8 +138,10 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { this.hasPathOrFail(); - const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); + const { isInWitnessBlock } = container.resolve(WitnessBlockContext); + // If we're inside a witness block, we only want to retrieve the state + // to use as a witness but not emit an ST if (!isInWitnessBlock) { const stateTransition = StateTransition.from(this.path, option); @@ -225,7 +177,7 @@ export class State extends Mixin(WithPath, WithStateServiceProvider) { toOption ); - const { isInWitnessBlock } = container.resolve(IsInWitnessBlockContext); + const { isInWitnessBlock } = container.resolve(WitnessBlockContext); if (isInWitnessBlock) { throw new Error("Cannot set state inside of provable block."); diff --git a/packages/protocol/src/state/WitnessBlockContext.ts b/packages/protocol/src/state/WitnessBlockContext.ts new file mode 100644 index 000000000..aafbfd242 --- /dev/null +++ b/packages/protocol/src/state/WitnessBlockContext.ts @@ -0,0 +1,50 @@ +import { container, singleton } from "tsyringe"; +import { Provable } from "o1js"; + +@singleton() +export class WitnessBlockContext { + public witnessBlockDepth: number = 0; + + public get isInWitnessBlock() { + return this.witnessBlockDepth > 0; + } +} + +const asyncProxyWitnessFunction = < + Ret, + F extends (...args: any[]) => Promise, +>( + originalFuncDef: F +) => { + return async (...args: Parameters) => { + const context = container.resolve(WitnessBlockContext); + context.witnessBlockDepth += 1; + const ret = await originalFuncDef(...args); + context.witnessBlockDepth -= 1; + return ret; + }; +}; + +const proxySyncWitnessFunction = < + Params extends any[], + Ret, + F extends (...args: Params) => Ret, +>( + originalFuncDef: F +) => { + return (...args: Params): Ret => { + const context = container.resolve(WitnessBlockContext); + context.witnessBlockDepth += 1; + const ret = originalFuncDef(...args); + context.witnessBlockDepth -= 1; + return ret; + }; +}; + +Provable.witnessAsync = asyncProxyWitnessFunction(Provable.witnessAsync); + +Provable.witness = proxySyncWitnessFunction(Provable.witness); + +Provable.witnessFields = proxySyncWitnessFunction(Provable.witnessFields); + +Provable.asProver = proxySyncWitnessFunction(Provable.asProver); diff --git a/packages/sdk/test/stprover-emit-sts.test.ts b/packages/sdk/test/stprover-emit-sts.test.ts index b50f0de41..ef9170aed 100644 --- a/packages/sdk/test/stprover-emit-sts.test.ts +++ b/packages/sdk/test/stprover-emit-sts.test.ts @@ -66,6 +66,10 @@ describe("StateTransition", () => { appChain.setSigner(senderKey); }); + afterEach(async () => { + await appChain.close(); + }); + it("should emit no sts for get", async () => { const stateTester = appChain.runtime.resolve("StateTester"); const context = container.resolve(RuntimeMethodExecutionContext); From 6274eb777501d30600fcfc8649570be8f9838866 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Thu, 27 Nov 2025 19:10:10 +0900 Subject: [PATCH 16/16] Fixed linting error --- packages/protocol/src/state/State.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/src/state/State.ts b/packages/protocol/src/state/State.ts index 003618d6f..2608a0c9f 100644 --- a/packages/protocol/src/state/State.ts +++ b/packages/protocol/src/state/State.ts @@ -1,6 +1,6 @@ import { Mixin } from "ts-mixer"; import { Bool, Field, Provable, type FlexibleProvablePure, Struct } from "o1js"; -import { container, singleton } from "tsyringe"; +import { container } from "tsyringe"; import { dummyValue } from "@proto-kit/common"; import { Path } from "../model/Path";