From 7491d0cff0b37cba5e5b404b83616ada5e038be7 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 14:44:31 +0100 Subject: [PATCH 01/10] Separated TransactionProver from BlockProver: Protocol --- packages/protocol/src/index.ts | 3 + packages/protocol/src/protocol/Protocol.ts | 14 + .../src/protocol/ProvableBlockHook.ts | 76 ++- .../src/protocol/ProvableTransactionHook.ts | 70 ++- .../src/prover/block/BlockProvable.ts | 70 +-- .../protocol/src/prover/block/BlockProver.ts | 513 +--------------- .../prover/transaction/TransactionProvable.ts | 178 ++++++ .../prover/transaction/TransactionProver.ts | 573 ++++++++++++++++++ packages/protocol/src/prover/utils.ts | 106 ++++ .../modularity/ProvableSettlementHook.ts | 2 +- 10 files changed, 988 insertions(+), 617 deletions(-) create mode 100644 packages/protocol/src/prover/transaction/TransactionProvable.ts create mode 100644 packages/protocol/src/prover/transaction/TransactionProver.ts create mode 100644 packages/protocol/src/prover/utils.ts diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts index 080dfc646..8d8cf7d9d 100644 --- a/packages/protocol/src/index.ts +++ b/packages/protocol/src/index.ts @@ -29,6 +29,9 @@ export * from "./prover/block/accummulators/BlockHashMerkleTree"; export * from "./prover/block/services/RuntimeVerificationKeyRootService"; export * from "./prover/statetransition/StateTransitionProver"; export * from "./prover/statetransition/StateTransitionProvable"; +export * from "./prover/transaction/TransactionProver"; +export * from "./prover/transaction/TransactionProvable"; +export * from "./prover/utils"; export * from "./protocol/Protocol"; export * from "./protocol/ProtocolModule"; export * from "./protocol/ProtocolEnvironment"; diff --git a/packages/protocol/src/protocol/Protocol.ts b/packages/protocol/src/protocol/Protocol.ts index 0d1496d86..cbdf4c130 100644 --- a/packages/protocol/src/protocol/Protocol.ts +++ b/packages/protocol/src/protocol/Protocol.ts @@ -21,6 +21,7 @@ import { ProvableSettlementHook } from "../settlement/modularity/ProvableSettlem import { NoopSettlementHook } from "../hooks/NoopSettlementHook"; import { AccountStateHook } from "../hooks/AccountStateHook"; import { NoopTransactionHook } from "../hooks/NoopTransactionHook"; +import { TransactionProvable } from "../prover/transaction/TransactionProvable"; import { ProtocolModule } from "./ProtocolModule"; import { ProvableTransactionHook } from "./ProvableTransactionHook"; @@ -44,6 +45,10 @@ export type ProtocolModulesRecord = ModulesRecord< TypedClass> >; +export interface TransactionProverType + extends ProtocolModule, + TransactionProvable {} + export interface BlockProverType extends ProtocolModule, BlockProvable {} export interface StateTransitionProverType @@ -51,6 +56,7 @@ export interface StateTransitionProverType StateTransitionProvable {} export type MandatoryProtocolModulesRecord = { + TransactionProver: TypedClass; BlockProver: TypedClass; StateTransitionProver: TypedClass; AccountState: TypedClass; @@ -115,6 +121,14 @@ export class Protocol< return this.definition[moduleName] !== undefined; } + public get transactionProver(): TransactionProvable { + // Why do I resolve directly here? + // I don't know exactly but generics don't let me use .resolve() + return this.container.resolve>( + "TransactionProver" + ); + } + public get blockProver(): BlockProvable { // Why do I resolve directly here? // I don't know exactly but generics don't let me use .resolve() diff --git a/packages/protocol/src/protocol/ProvableBlockHook.ts b/packages/protocol/src/protocol/ProvableBlockHook.ts index b5381e193..b15863b92 100644 --- a/packages/protocol/src/protocol/ProvableBlockHook.ts +++ b/packages/protocol/src/protocol/ProvableBlockHook.ts @@ -2,54 +2,48 @@ import { Field } from "o1js"; import { NoConfig } from "@proto-kit/common"; import { NetworkState } from "../model/network/NetworkState"; -import { MethodPublicOutput } from "../model/MethodPublicOutput"; -import { BlockProverTransactionArguments } from "../prover/block/BlockProvable"; - -import { TransitioningProtocolModule } from "./TransitioningProtocolModule"; import { - AfterTransactionHookArguments, - BeforeTransactionHookArguments, - ProvableHookBlockState, - toProvableHookBlockState, -} from "./ProvableTransactionHook"; - -export interface BeforeBlockHookArguments extends ProvableHookBlockState {} - -export interface AfterBlockHookArguments extends BeforeBlockHookArguments { - stateRoot: Field; -} + BlockProverState, + BlockProverStateCommitments, +} from "../prover/block/BlockProvable"; -export function toBeforeTransactionHookArgument( - executionData: Omit< - BlockProverTransactionArguments, - "verificationKeyAttestation" - >, - networkState: NetworkState, - state: Parameters[0] -): BeforeTransactionHookArguments { - const { transaction, signature } = executionData; +import { TransitioningProtocolModule } from "./TransitioningProtocolModule"; +export type ProvableHookBlockState = Pick< + BlockProverStateCommitments, + | "transactionsHash" + | "eternalTransactionsHash" + | "incomingMessagesHash" + | "blockHashRoot" +>; + +export function toProvableHookBlockState( + state: Pick< + BlockProverState, + | "transactionList" + | "eternalTransactionsList" + | "incomingMessages" + | "blockHashRoot" + > +) { + const { + transactionList, + eternalTransactionsList, + incomingMessages, + blockHashRoot, + } = state; return { - networkState, - transaction, - signature, - prover: toProvableHookBlockState(state), + transactionsHash: transactionList.commitment, + eternalTransactionsHash: eternalTransactionsList.commitment, + incomingMessagesHash: incomingMessages.commitment, + blockHashRoot, }; } -export function toAfterTransactionHookArgument( - executionData: Omit< - BlockProverTransactionArguments, - "verificationKeyAttestation" - >, - networkState: NetworkState, - state: Parameters[0], - runtimeResult: MethodPublicOutput -): AfterTransactionHookArguments { - return { - ...toBeforeTransactionHookArgument(executionData, networkState, state), - runtimeResult, - }; +export interface BeforeBlockHookArguments extends ProvableHookBlockState {} + +export interface AfterBlockHookArguments extends BeforeBlockHookArguments { + stateRoot: Field; } // Purpose is to build transition from -> to network state diff --git a/packages/protocol/src/protocol/ProvableTransactionHook.ts b/packages/protocol/src/protocol/ProvableTransactionHook.ts index 32aec366f..68fa3bedf 100644 --- a/packages/protocol/src/protocol/ProvableTransactionHook.ts +++ b/packages/protocol/src/protocol/ProvableTransactionHook.ts @@ -4,41 +4,63 @@ import { Signature } from "o1js"; import { RuntimeTransaction } from "../model/transaction/RuntimeTransaction"; import { NetworkState } from "../model/network/NetworkState"; import { MethodPublicOutput } from "../model/MethodPublicOutput"; -import type { - BlockProverState, - BlockProverStateCommitments, -} from "../prover/block/BlockProvable"; +import { + TransactionProverPublicInput, + TransactionProverState, + TransactionProverTransactionArguments, +} from "../prover/transaction/TransactionProvable"; import { TransitioningProtocolModule } from "./TransitioningProtocolModule"; -export type ProvableHookBlockState = Pick< - BlockProverStateCommitments, - | "transactionsHash" - | "eternalTransactionsHash" - | "incomingMessagesHash" - | "blockHashRoot" +export type ProvableHookTransactionState = Pick< + TransactionProverPublicInput, + "transactionsHash" | "eternalTransactionsHash" | "incomingMessagesHash" >; -export function toProvableHookBlockState( +export function toProvableHookTransactionState( state: Pick< - BlockProverState, - | "transactionList" - | "eternalTransactionsList" - | "incomingMessages" - | "blockHashRoot" + TransactionProverState, + "transactionList" | "eternalTransactionsList" | "incomingMessages" > ) { - const { - transactionList, - eternalTransactionsList, - incomingMessages, - blockHashRoot, - } = state; + const { transactionList, eternalTransactionsList, incomingMessages } = state; return { transactionsHash: transactionList.commitment, eternalTransactionsHash: eternalTransactionsList.commitment, incomingMessagesHash: incomingMessages.commitment, - blockHashRoot, + }; +} + +export function toBeforeTransactionHookArgument( + executionData: Omit< + TransactionProverTransactionArguments, + "verificationKeyAttestation" + >, + networkState: NetworkState, + state: Parameters[0] +): BeforeTransactionHookArguments { + const { transaction, signature } = executionData; + + return { + networkState, + transaction, + signature, + prover: toProvableHookTransactionState(state), + }; +} + +export function toAfterTransactionHookArgument( + executionData: Omit< + TransactionProverTransactionArguments, + "verificationKeyAttestation" + >, + networkState: NetworkState, + state: Parameters[0], + runtimeResult: MethodPublicOutput +): AfterTransactionHookArguments { + return { + ...toBeforeTransactionHookArgument(executionData, networkState, state), + runtimeResult, }; } @@ -56,7 +78,7 @@ export interface BeforeTransactionHookArguments { transaction: RuntimeTransaction; signature: Signature; networkState: NetworkState; - prover: ProvableHookBlockState; + prover: ProvableHookTransactionState; } export interface AfterTransactionHookArguments diff --git a/packages/protocol/src/prover/block/BlockProvable.ts b/packages/protocol/src/prover/block/BlockProvable.ts index 84d6493af..c7f7299ba 100644 --- a/packages/protocol/src/prover/block/BlockProvable.ts +++ b/packages/protocol/src/prover/block/BlockProvable.ts @@ -1,18 +1,7 @@ -// eslint-disable-next-line max-classes-per-file -import { - Bool, - DynamicProof, - Field, - Proof, - Signature, - Struct, - Void, -} from "o1js"; -import { WithZkProgrammable, CompilableModule } from "@proto-kit/common"; +import { Bool, Field, Proof, Struct } from "o1js"; +import { CompilableModule, WithZkProgrammable } from "@proto-kit/common"; import { StateTransitionProof } from "../statetransition/StateTransitionProvable"; -import { MethodPublicOutput } from "../../model/MethodPublicOutput"; -import { RuntimeTransaction } from "../../model/transaction/RuntimeTransaction"; import { NetworkState } from "../../model/network/NetworkState"; import { TransactionHashList } from "../accumulators/TransactionHashList"; import { MinaActionsHashList } from "../../utils/MinaPrefixedProvableHashList"; @@ -21,9 +10,9 @@ import { WitnessedRootHashList, WitnessedRootWitness, } from "../accumulators/WitnessedRootHashList"; +import { TransactionProof } from "../transaction/TransactionProvable"; import { BlockHashMerkleTreeWitness } from "./accummulators/BlockHashMerkleTree"; -import { RuntimeVerificationKeyAttestation } from "./accummulators/RuntimeVerificationKeyTree"; // Should be equal to BlockProver.PublicInput export interface BlockProverState { @@ -150,56 +139,11 @@ export class BlockProverPublicOutput extends Struct({ } } -export type BlockProverProof = Proof< - BlockProverPublicInput, - BlockProverPublicOutput ->; - -export class BlockProverTransactionArguments extends Struct({ - transaction: RuntimeTransaction, - signature: Signature, - verificationKeyAttestation: RuntimeVerificationKeyAttestation, -}) {} - -export class DynamicRuntimeProof extends DynamicProof< - Void, - MethodPublicOutput -> { - static publicInputType = Void; - - static publicOutputType = MethodPublicOutput; - - // TODO this won't be 0 for proofs-as-args - static maxProofsVerified = 0 as const; -} - -export class BlockProverSingleTransactionExecutionData extends Struct({ - transaction: BlockProverTransactionArguments, - networkState: NetworkState, -}) {} - -export class BlockProverMultiTransactionExecutionData extends Struct({ - transaction1: BlockProverTransactionArguments, - transaction2: BlockProverTransactionArguments, - networkState: NetworkState, -}) {} +export type BlockProof = Proof; export interface BlockProvable extends WithZkProgrammable, CompilableModule { - proveTransaction: ( - publicInput: BlockProverPublicInput, - runtimeProof: DynamicRuntimeProof, - executionData: BlockProverSingleTransactionExecutionData - ) => Promise; - - proveTransactions: ( - publicInput: BlockProverPublicInput, - runtimeProof1: DynamicRuntimeProof, - runtimeProof2: DynamicRuntimeProof, - executionData: BlockProverMultiTransactionExecutionData - ) => Promise; - proveBlock: ( publicInput: BlockProverPublicInput, networkState: NetworkState, @@ -207,12 +151,12 @@ export interface BlockProvable stateTransitionProof: StateTransitionProof, deferSTs: Bool, afterBlockRootWitness: WitnessedRootWitness, - transactionProof: BlockProverProof + transactionProof: TransactionProof ) => Promise; merge: ( publicInput: BlockProverPublicInput, - proof1: BlockProverProof, - proof2: BlockProverProof + proof1: BlockProof, + proof2: BlockProof ) => Promise; } diff --git a/packages/protocol/src/prover/block/BlockProver.ts b/packages/protocol/src/prover/block/BlockProver.ts index f97edd403..6b3d1e02d 100644 --- a/packages/protocol/src/prover/block/BlockProver.ts +++ b/packages/protocol/src/prover/block/BlockProver.ts @@ -1,12 +1,4 @@ -import { - Bool, - Field, - Proof, - Provable, - SelfProof, - VerificationKey, - ZkProgram, -} from "o1js"; +import { Bool, Field, Provable, SelfProof, ZkProgram } from "o1js"; import { container, inject, injectable, injectAll } from "tsyringe"; import { AreProofsEnabled, @@ -30,58 +22,34 @@ import { StateTransitionProverPublicOutput, } from "../statetransition/StateTransitionProvable"; import { RuntimeTransaction } from "../../model/transaction/RuntimeTransaction"; -import { - ProvableStateTransition, - StateTransition, -} from "../../model/StateTransition"; -import { - AfterTransactionHookArguments, - BeforeTransactionHookArguments, - ProvableTransactionHook, - toProvableHookBlockState, -} from "../../protocol/ProvableTransactionHook"; -import { - RuntimeMethodExecutionContext, - RuntimeMethodExecutionData, -} from "../../state/context/RuntimeMethodExecutionContext"; +import { RuntimeMethodExecutionContext } from "../../state/context/RuntimeMethodExecutionContext"; import { AfterBlockHookArguments, BeforeBlockHookArguments, ProvableBlockHook, - toAfterTransactionHookArgument, - toBeforeTransactionHookArgument, + toProvableHookBlockState, } from "../../protocol/ProvableBlockHook"; import { NetworkState } from "../../model/network/NetworkState"; -import { SignedTransaction } from "../../model/transaction/SignedTransaction"; -import { MinaActions } from "../../utils/MinaPrefixedProvableHashList"; -import { StateTransitionReductionList } from "../accumulators/StateTransitionReductionList"; import { assertEqualsIf } from "../../utils/utils"; import { WitnessedRootWitness } from "../accumulators/WitnessedRootHashList"; import { StateServiceProvider } from "../../state/StateServiceProvider"; -import { AppliedStateTransitionBatch } from "../../model/AppliedStateTransitionBatch"; +import { executeHooks } from "../utils"; +import { + TransactionProof, + TransactionProverPublicInput, +} from "../transaction/TransactionProvable"; import { BlockProvable, - BlockProverProof, + BlockProof, BlockProverPublicInput, BlockProverPublicOutput, - DynamicRuntimeProof, - BlockProverMultiTransactionExecutionData, - BlockProverTransactionArguments, - BlockProverSingleTransactionExecutionData, - BlockProverState, BlockProverStateCommitments, } from "./BlockProvable"; import { BlockHashMerkleTreeWitness, BlockHashTreeEntry, } from "./accummulators/BlockHashMerkleTree"; -import { - MethodVKConfigData, - MinimalVKTreeService, - RuntimeVerificationKeyAttestation, -} from "./accummulators/RuntimeVerificationKeyTree"; -import { RuntimeVerificationKeyRootService } from "./services/RuntimeVerificationKeyRootService"; const errors = { propertyNotMatchingStep: (propertyName: string, step: string) => @@ -97,18 +65,8 @@ const errors = { networkStateHashNotMatching: (step: string) => errors.propertyNotMatchingStep("Network state hash", step), - - invalidZkProgramTreeRoot: () => - "Root hash of the provided zkProgram config witness is invalid", }; -type ApplyTransactionArguments = Omit< - BlockProverTransactionArguments, - "verificationKeyAttestation" ->; - -export type BlockProof = Proof; - export class BlockProverProgrammable extends ZkProgrammable< BlockProverPublicInput, BlockProverPublicOutput @@ -119,10 +77,8 @@ export class BlockProverProgrammable extends ZkProgrammable< StateTransitionProverPublicInput, StateTransitionProverPublicOutput >, - private readonly transactionHooks: ProvableTransactionHook[], private readonly blockHooks: ProvableBlockHook[], - private readonly stateServiceProvider: StateServiceProvider, - private readonly verificationKeyService: MinimalVKTreeService + private readonly stateServiceProvider: StateServiceProvider ) { super(); } @@ -133,203 +89,6 @@ export class BlockProverProgrammable extends ZkProgrammable< return this.prover.areProofsEnabled; } - /** - * Applies and checks the two proofs and applies the corresponding state - * changes to the given state. - * - * The rough high level workflow of this function: - * 1. Execute beforeTransaction hooks, pushing the ST batch - * 2. Add Transaction to bundle, meaning appending it to all the respective commitments - * 3. Push the runtime ST batch - * 4. Execute afterTransaction hooks, pushing the ST batch - * 5. Some consistency checks and signature verification - * - * @param fromState The from-state of the BlockProver - * @param runtimeOutput - * @param executionData - * @param networkState - * @returns The new BlockProver-state to be used as public output - */ - public async applyTransaction( - fromState: BlockProverState, - runtimeOutput: MethodPublicOutput, - executionData: ApplyTransactionArguments, - networkState: NetworkState - ): Promise { - const { transaction, signature } = executionData; - - let state = { ...fromState }; - - const { isMessage } = runtimeOutput; - - const beforeTxHookArguments = toBeforeTransactionHookArgument( - executionData, - networkState, - state - ); - - // Apply beforeTransaction hook state transitions - const beforeBatch = await this.executeTransactionHooks( - async (module, args) => await module.beforeTransaction(args), - beforeTxHookArguments, - isMessage - ); - - state = this.addTransactionToBundle( - state, - runtimeOutput.isMessage, - transaction - ); - - state.pendingSTBatches.push(beforeBatch); - - state.pendingSTBatches.push({ - batchHash: runtimeOutput.stateTransitionsHash, - applied: runtimeOutput.status, - }); - - // Apply afterTransaction hook state transitions - const afterTxHookArguments = toAfterTransactionHookArgument( - executionData, - networkState, - state, - runtimeOutput - ); - - // Switch to different state set for afterTx hooks - this.stateServiceProvider.popCurrentStateService(); - - const afterBatch = await this.executeTransactionHooks( - async (module, args) => await module.afterTransaction(args), - afterTxHookArguments, - isMessage - ); - state.pendingSTBatches.push(afterBatch); - - // Check transaction integrity against appProof - const blockTransactionHash = transaction.hash(); - - blockTransactionHash.assertEquals( - runtimeOutput.transactionHash, - "Transactions provided in AppProof and BlockProof do not match" - ); - - // Check transaction signature - new SignedTransaction({ - transaction, - signature, - }) - .validateSignature() - .or(isMessage) - .assertTrue("Transaction signature not valid"); - - // Validate layout of transaction witness - transaction.assertTransactionType(isMessage); - - // Check network state integrity against appProof - state.networkState - .hash() - .assertEquals( - runtimeOutput.networkStateHash, - "Network state does not match state used in AppProof" - ); - - return state; - } - - // eslint-disable-next-line max-len - // TODO How does this interact with the RuntimeMethodExecutionContext when executing runtimemethods? - - /** - * Constructs a AppliedBatch based on a list of STs and the flag whether to - * be applied or not. The AppliedBatch is a condensed commitment to a batch - * of STs. - */ - private constructBatch( - stateTransitions: StateTransition[], - applied: Bool - ) { - const transitions = stateTransitions.map((transition) => - transition.toProvable() - ); - - const hashList = new StateTransitionReductionList(ProvableStateTransition); - transitions.forEach((transition) => { - hashList.push(transition); - }); - - return new AppliedStateTransitionBatch({ - batchHash: hashList.commitment, - applied, - }); - } - - private async executeTransactionHooks< - T extends BeforeTransactionHookArguments | AfterTransactionHookArguments, - >( - hook: (module: ProvableTransactionHook, args: T) => Promise, - hookArguments: T, - isMessage: Bool - ) { - const { batch, rawStatus } = await this.executeHooks( - hookArguments, - async () => { - for (const module of this.transactionHooks) { - // eslint-disable-next-line no-await-in-loop - await hook(module, hookArguments); - } - }, - isMessage - ); - - // This is going to set applied to false in case the hook fails - // (that's only possible for messages though as others are hard-asserted) - batch.applied = rawStatus; - - return batch; - } - - private async executeHooks( - contextArguments: RuntimeMethodExecutionData, - method: () => Promise, - isMessage: Bool | undefined = undefined - ) { - const executionContext = container.resolve(RuntimeMethodExecutionContext); - executionContext.clear(); - - // Setup context for potential calls to runtime methods. - // This way they can use this.transaction etc. while still having provable - // integrity between data - executionContext.setup(contextArguments); - executionContext.beforeMethod("", "", []); - - const result = await method(); - - executionContext.afterMethod(); - - const { stateTransitions, status, statusMessage } = - executionContext.current().result; - - // See https://github.com/proto-kit/framework/issues/321 for why we do this here - if (isMessage !== undefined) { - // isMessage is defined for all tx hooks - status - .or(isMessage) - .assertTrue( - `Transaction hook call failed for non-message tx: ${statusMessage ?? "-"}` - ); - } else { - // isMessage is undefined for all block hooks - status.assertTrue(`Block hook call failed: ${statusMessage ?? "-"}`); - } - - return { - batch: this.constructBatch(stateTransitions, Bool(true)), - result, - rawStatus: status, - }; - } - public async executeBlockHooks< T extends BeforeBlockHookArguments | AfterBlockHookArguments, >( @@ -347,7 +106,7 @@ export class BlockProverProgrammable extends ZkProgrammable< networkState: inputNetworkState, }; - return await this.executeHooks(startingInputs, async () => { + return await executeHooks(startingInputs, async () => { const executionContext = container.resolve(RuntimeMethodExecutionContext); return await this.blockHooks.reduce>( @@ -369,135 +128,6 @@ export class BlockProverProgrammable extends ZkProgrammable< }); } - public addTransactionToBundle< - T extends Pick< - BlockProverState, - "transactionList" | "eternalTransactionsList" | "incomingMessages" - >, - >(state: T, isMessage: Bool, transaction: RuntimeTransaction): T { - const transactionHash = transaction.hash(); - - // Append tx to transaction list - state.transactionList.pushIf(transactionHash, isMessage.not()); - - // Append tx to eternal transaction list - // TODO Change that to the a sequence-state compatible transaction struct - state.eternalTransactionsList.push(transactionHash); - - // Append tx to incomingMessagesHash - const actionHash = MinaActions.actionHash(transaction.hashData()); - - state.incomingMessages.pushIf(actionHash, isMessage); - - return state; - } - - private verifyVerificationKeyAttestation( - attestation: RuntimeVerificationKeyAttestation, - methodId: Field - ): VerificationKey { - // Verify the [methodId, vk] tuple against the baked-in vk tree root - const { verificationKey, witness: verificationKeyTreeWitness } = - attestation; - - const root = Field(this.verificationKeyService.getRoot()); - const calculatedRoot = verificationKeyTreeWitness.calculateRoot( - new MethodVKConfigData({ - methodId: methodId, - vkHash: verificationKey.hash, - }).hash() - ); - root.assertEquals(calculatedRoot, errors.invalidZkProgramTreeRoot()); - - return verificationKey; - } - - public async proveTransactionInternal( - fromState: BlockProverState, - runtimeProof: DynamicRuntimeProof, - { transaction, networkState }: BlockProverSingleTransactionExecutionData - ): Promise { - const verificationKey = this.verifyVerificationKeyAttestation( - transaction.verificationKeyAttestation, - transaction.transaction.methodId - ); - - runtimeProof.verify(verificationKey); - - return await this.applyTransaction( - fromState, - runtimeProof.publicOutput, - transaction, - networkState - ); - } - - private staticChecks(publicInput: BlockProverPublicInput) { - publicInput.blockNumber.assertEquals( - MAX_FIELD, - "blockNumber has to be MAX for transaction proofs" - ); - } - - @provableMethod() - public async proveTransaction( - publicInput: BlockProverPublicInput, - runtimeProof: DynamicRuntimeProof, - executionData: BlockProverSingleTransactionExecutionData - ): Promise { - const state = BlockProverStateCommitments.toBlockProverState( - publicInput, - executionData.networkState - ); - - this.staticChecks(publicInput); - - const stateTo = await this.proveTransactionInternal( - state, - runtimeProof, - executionData - ); - - return new BlockProverPublicOutput({ - ...BlockProverStateCommitments.fromBlockProverState(stateTo), - closed: Bool(false), - }); - } - - @provableMethod() - public async proveTransactions( - publicInput: BlockProverPublicInput, - runtimeProof1: DynamicRuntimeProof, - runtimeProof2: DynamicRuntimeProof, - executionData: BlockProverMultiTransactionExecutionData - ): Promise { - const state = BlockProverStateCommitments.toBlockProverState( - publicInput, - executionData.networkState - ); - - this.staticChecks(publicInput); - - const state1 = await this.proveTransactionInternal(state, runtimeProof1, { - transaction: executionData.transaction1, - networkState: executionData.networkState, - }); - - // Switch to next state record for 2nd tx beforeTx hook - // TODO Can be prevented by merging 1st afterTx + 2nd beforeTx - this.stateServiceProvider.popCurrentStateService(); - - const stateTo = await this.proveTransactionInternal(state1, runtimeProof2, { - transaction: executionData.transaction2, - networkState: executionData.networkState, - }); - - return new BlockProverPublicOutput({ - ...BlockProverStateCommitments.fromBlockProverState(stateTo), - closed: Bool(false), - }); - } - public includeSTProof( stateTransitionProof: StateTransitionProof, apply: Bool, @@ -588,7 +218,7 @@ export class BlockProverProgrammable extends ZkProgrammable< stateTransitionProof: StateTransitionProof, deferSTProof: Bool, afterBlockRootWitness: WitnessedRootWitness, - transactionProof: BlockProverProof + transactionProof: TransactionProof ): Promise { // 1. Make assertions about the inputs publicInput.transactionsHash.assertEquals( @@ -597,14 +227,6 @@ export class BlockProverProgrammable extends ZkProgrammable< ); // TransactionProof format checks - transactionProof.publicInput.blockHashRoot.assertEquals( - Field(0), - "TransactionProof cannot carry the blockHashRoot - publicInput" - ); - transactionProof.publicOutput.blockHashRoot.assertEquals( - Field(0), - "TransactionProof cannot carry the blockHashRoot - publicOutput" - ); transactionProof.publicInput.networkStateHash.assertEquals( transactionProof.publicOutput.networkStateHash, "TransactionProof cannot alter the network state" @@ -619,10 +241,9 @@ export class BlockProverProgrammable extends ZkProgrammable< // input and output doesn't match fully // We have to compare the whole input and output because we can make no // assumptions about the values, since it can be an arbitrary dummy-proof - const txProofOutput = transactionProof.publicOutput; - const isEmptyTransition = txProofOutput.equals( - transactionProof.publicInput, - txProofOutput.closed + const isEmptyTransition = TransactionProverPublicInput.equals( + transactionProof.publicOutput, + transactionProof.publicInput ); const skipTransactionProofVerification = isEmptyTransition; const verifyTransactionProof = isEmptyTransition.not(); @@ -647,10 +268,6 @@ export class BlockProverProgrammable extends ZkProgrammable< .assertTrue( "TransactionProof networkstate hash not matching beforeBlock hook result" ); - transactionProof.publicInput.stateRoot.assertEquals( - transactionProof.publicOutput.stateRoot, - "TransactionProofs can't change the state root" - ); // Check that the transaction proof's STs start after the beforeBlock hook transactionProof.publicInput.pendingSTBatchesHash.assertEquals( @@ -767,8 +384,8 @@ export class BlockProverProgrammable extends ZkProgrammable< @provableMethod() public async merge( publicInput: BlockProverPublicInput, - proof1: BlockProverProof, - proof2: BlockProverProof + proof1: BlockProof, + proof2: BlockProof ): Promise { proof1.verify(); proof2.verify(); @@ -935,8 +552,6 @@ export class BlockProverProgrammable extends ZkProgrammable< >[] { const { prover, stateTransitionProver } = this; const StateTransitionProofClass = stateTransitionProver.zkProgram[0].Proof; - const proveTransaction = prover.proveTransaction.bind(prover); - const proveTransactions = prover.proveTransactions.bind(prover); const proveBlock = prover.proveBlock.bind(prover); const merge = prover.merge.bind(prover); @@ -946,51 +561,6 @@ export class BlockProverProgrammable extends ZkProgrammable< publicOutput: BlockProverPublicOutput, methods: { - proveTransaction: { - privateInputs: [ - DynamicRuntimeProof, - BlockProverSingleTransactionExecutionData, - ], - - async method( - publicInput: BlockProverPublicInput, - runtimeProof: DynamicRuntimeProof, - executionData: BlockProverSingleTransactionExecutionData - ) { - return { - publicOutput: await proveTransaction( - publicInput, - runtimeProof, - executionData - ), - }; - }, - }, - - proveTransactions: { - privateInputs: [ - DynamicRuntimeProof, - DynamicRuntimeProof, - BlockProverMultiTransactionExecutionData, - ], - - async method( - publicInput: BlockProverPublicInput, - runtimeProof1: DynamicRuntimeProof, - runtimeProof2: DynamicRuntimeProof, - executionData: BlockProverMultiTransactionExecutionData - ) { - return { - publicOutput: await proveTransactions( - publicInput, - runtimeProof1, - runtimeProof2, - executionData - ), - }; - }, - }, - proveBlock: { privateInputs: [ NetworkState, @@ -1007,7 +577,7 @@ export class BlockProverProgrammable extends ZkProgrammable< stateTransitionProof: StateTransitionProof, deferSTs: Bool, afterBlockRootWitness: WitnessedRootWitness, - transactionProof: BlockProverProof + transactionProof: BlockProof ) { return { publicOutput: await proveBlock( @@ -1031,8 +601,8 @@ export class BlockProverProgrammable extends ZkProgrammable< async method( publicInput: BlockProverPublicInput, - proof1: BlockProverProof, - proof2: BlockProverProof + proof1: BlockProof, + proof2: BlockProof ) { return { publicOutput: await merge(publicInput, proof1, proof2) }; }, @@ -1041,8 +611,6 @@ export class BlockProverProgrammable extends ZkProgrammable< }); const methods = { - proveTransaction: program.proveTransaction, - proveTransactions: program.proveTransactions, proveBlock: program.proveBlock, merge: program.merge, }; @@ -1084,22 +652,17 @@ export class BlockProver @inject("Runtime") public readonly runtime: WithZkProgrammable & CompilableModule, - @injectAll("ProvableTransactionHook") - transactionHooks: ProvableTransactionHook[], @injectAll("ProvableBlockHook") blockHooks: ProvableBlockHook[], @inject("StateServiceProvider") - stateServiceProvider: StateServiceProvider, - verificationKeyService: RuntimeVerificationKeyRootService + stateServiceProvider: StateServiceProvider ) { super(); this.zkProgrammable = new BlockProverProgrammable( this, stateTransitionProver.zkProgrammable, - transactionHooks, blockHooks, - stateServiceProvider, - verificationKeyService + stateServiceProvider ); } @@ -1113,32 +676,6 @@ export class BlockProver }); } - public proveTransaction( - publicInput: BlockProverPublicInput, - runtimeProof: DynamicRuntimeProof, - executionData: BlockProverSingleTransactionExecutionData - ): Promise { - return this.zkProgrammable.proveTransaction( - publicInput, - runtimeProof, - executionData - ); - } - - public proveTransactions( - publicInput: BlockProverPublicInput, - runtimeProof1: DynamicRuntimeProof, - runtimeProof2: DynamicRuntimeProof, - executionData: BlockProverMultiTransactionExecutionData - ): Promise { - return this.zkProgrammable.proveTransactions( - publicInput, - runtimeProof1, - runtimeProof2, - executionData - ); - } - public proveBlock( publicInput: BlockProverPublicInput, networkState: NetworkState, @@ -1146,7 +683,7 @@ export class BlockProver stateTransitionProof: StateTransitionProof, deferSTs: Bool, afterBlockRootWitness: WitnessedRootWitness, - transactionProof: BlockProverProof + transactionProof: TransactionProof ): Promise { return this.zkProgrammable.proveBlock( publicInput, @@ -1161,8 +698,8 @@ export class BlockProver public merge( publicInput: BlockProverPublicInput, - proof1: BlockProverProof, - proof2: BlockProverProof + proof1: BlockProof, + proof2: BlockProof ): Promise { return this.zkProgrammable.merge(publicInput, proof1, proof2); } diff --git a/packages/protocol/src/prover/transaction/TransactionProvable.ts b/packages/protocol/src/prover/transaction/TransactionProvable.ts new file mode 100644 index 000000000..4180fd906 --- /dev/null +++ b/packages/protocol/src/prover/transaction/TransactionProvable.ts @@ -0,0 +1,178 @@ +// eslint-disable-next-line max-classes-per-file +import { CompilableModule, WithZkProgrammable } from "@proto-kit/common"; +import { DynamicProof, Field, Proof, Signature, Struct, Void } from "o1js"; + +import { RuntimeTransaction } from "../../model/transaction/RuntimeTransaction"; +import { RuntimeVerificationKeyAttestation } from "../block/accummulators/RuntimeVerificationKeyTree"; +import { MethodPublicOutput } from "../../model/MethodPublicOutput"; +import { NetworkState } from "../../model/network/NetworkState"; +import { TransactionHashList } from "../accumulators/TransactionHashList"; +import { AppliedBatchHashList } from "../accumulators/AppliedBatchHashList"; +import { MinaActionsHashList } from "../../utils/MinaPrefixedProvableHashList"; +import { WitnessedRootHashList } from "../accumulators/WitnessedRootHashList"; + +export class TransactionProverState { + /** + * The current commitment of the transaction-list which + * will at the end equal the bundle hash + */ + transactionList: TransactionHashList; + + /** + * The network state which gives access to values such as blockHeight + * This value is the same for the whole batch (L2 block) + */ + networkState: NetworkState; + + /** + * A variant of the transactionsHash that is never reset. + * Thought for usage in the sequence state mempool. + * In comparison, transactionsHash restarts at 0 for every new block + */ + eternalTransactionsList: TransactionHashList; + + pendingSTBatches: AppliedBatchHashList; + + incomingMessages: MinaActionsHashList; + + witnessedRoots: WitnessedRootHashList; + + constructor(args: { + transactionList: TransactionHashList; + networkState: NetworkState; + eternalTransactionsList: TransactionHashList; + pendingSTBatches: AppliedBatchHashList; + incomingMessages: MinaActionsHashList; + witnessedRoots: WitnessedRootHashList; + }) { + this.transactionList = args.transactionList; + this.networkState = args.networkState; + this.eternalTransactionsList = args.eternalTransactionsList; + this.pendingSTBatches = args.pendingSTBatches; + this.incomingMessages = args.incomingMessages; + this.witnessedRoots = args.witnessedRoots; + } + + public toCommitments(): TransactionProverPublicInput { + return { + networkStateHash: this.networkState.hash(), + pendingSTBatchesHash: this.pendingSTBatches.commitment, + transactionsHash: this.transactionList.commitment, + eternalTransactionsHash: this.eternalTransactionsList.commitment, + incomingMessagesHash: this.incomingMessages.commitment, + witnessedRootsHash: this.witnessedRoots.commitment, + }; + } + + public static fromCommitments( + publicInput: TransactionProverPublicInput, + networkState: NetworkState + ): TransactionProverState { + publicInput.networkStateHash.assertEquals( + networkState.hash(), + "ExecutionData Networkstate doesn't equal public input hash" + ); + + return new TransactionProverState({ + networkState, + transactionList: new TransactionHashList(publicInput.transactionsHash), + eternalTransactionsList: new TransactionHashList( + publicInput.eternalTransactionsHash + ), + incomingMessages: new MinaActionsHashList( + publicInput.incomingMessagesHash + ), + pendingSTBatches: new AppliedBatchHashList( + publicInput.pendingSTBatchesHash + ), + witnessedRoots: new WitnessedRootHashList(publicInput.witnessedRootsHash), + }); + } +} + +export const TransactionProverStateCommitments = { + transactionsHash: Field, + // Commitment to the list of unprocessed (pending) batches of STs that need to be proven + pendingSTBatchesHash: Field, + witnessedRootsHash: Field, + networkStateHash: Field, + eternalTransactionsHash: Field, + incomingMessagesHash: Field, +}; + +export class TransactionProverPublicInput extends Struct( + TransactionProverStateCommitments +) { + public static equals( + input1: TransactionProverPublicInput, + input2: TransactionProverPublicInput + ) { + const output2 = TransactionProverPublicInput.toFields(input2); + const output1 = TransactionProverPublicInput.toFields(input1); + return output1 + .map((value1, index) => value1.equals(output2[index])) + .reduce((a, b) => a.and(b)); + } +} + +export class TransactionProverPublicOutput extends TransactionProverPublicInput {} + +export class TransactionProverTransactionArguments extends Struct({ + transaction: RuntimeTransaction, + signature: Signature, + verificationKeyAttestation: RuntimeVerificationKeyAttestation, +}) {} + +export class DynamicRuntimeProof extends DynamicProof< + Void, + MethodPublicOutput +> { + static publicInputType = Void; + + static publicOutputType = MethodPublicOutput; + + // TODO this won't be 0 for proofs-as-args + static maxProofsVerified = 0 as const; +} + +export class BlockProverSingleTransactionExecutionData extends Struct({ + transaction: TransactionProverTransactionArguments, + networkState: NetworkState, +}) {} + +export class BlockProverMultiTransactionExecutionData extends Struct({ + transaction1: TransactionProverTransactionArguments, + transaction2: TransactionProverTransactionArguments, + networkState: NetworkState, +}) {} + +export type TransactionProof = Proof< + TransactionProverPublicInput, + TransactionProverPublicOutput +>; + +export interface TransactionProvable + extends WithZkProgrammable< + TransactionProverPublicInput, + TransactionProverPublicOutput + >, + CompilableModule { + proveTransaction: ( + publicInput: TransactionProverPublicInput, + runtimeProof: DynamicRuntimeProof, + executionData: BlockProverSingleTransactionExecutionData + ) => Promise; + + proveTransactions: ( + publicInput: TransactionProverPublicInput, + runtimeProof1: DynamicRuntimeProof, + runtimeProof2: DynamicRuntimeProof, + executionData: BlockProverMultiTransactionExecutionData + ) => Promise; + + merge: ( + publicInput: TransactionProverPublicInput, + proof1: TransactionProof, + proof2: TransactionProof + ) => Promise; +} diff --git a/packages/protocol/src/prover/transaction/TransactionProver.ts b/packages/protocol/src/prover/transaction/TransactionProver.ts new file mode 100644 index 000000000..9294edda8 --- /dev/null +++ b/packages/protocol/src/prover/transaction/TransactionProver.ts @@ -0,0 +1,573 @@ +import { + AreProofsEnabled, + CompilableModule, + CompileArtifact, + CompileRegistry, + PlainZkProgram, + provableMethod, + WithZkProgrammable, + ZkProgrammable, +} from "@proto-kit/common"; +import { Bool, Field, SelfProof, VerificationKey, ZkProgram } from "o1js"; +import { inject, injectable, injectAll } from "tsyringe"; + +import { NetworkState } from "../../model/network/NetworkState"; +import { ProtocolModule } from "../../protocol/ProtocolModule"; +import { MethodPublicOutput } from "../../model/MethodPublicOutput"; +import { + AfterTransactionHookArguments, + BeforeTransactionHookArguments, + ProvableTransactionHook, + toAfterTransactionHookArgument, + toBeforeTransactionHookArgument, +} from "../../protocol/ProvableTransactionHook"; +import { StateServiceProvider } from "../../state/StateServiceProvider"; +import { RuntimeVerificationKeyRootService } from "../block/services/RuntimeVerificationKeyRootService"; +import { addTransactionToBundle, executeHooks } from "../utils"; +import { SignedTransaction } from "../../model/transaction/SignedTransaction"; +import { + MethodVKConfigData, + MinimalVKTreeService, + RuntimeVerificationKeyAttestation, +} from "../block/accummulators/RuntimeVerificationKeyTree"; + +import { + BlockProverMultiTransactionExecutionData, + BlockProverSingleTransactionExecutionData, + DynamicRuntimeProof, + TransactionProof, + TransactionProvable, + TransactionProverPublicInput, + TransactionProverPublicOutput, + TransactionProverState, + TransactionProverTransactionArguments, +} from "./TransactionProvable"; + +const errors = { + invalidZkProgramTreeRoot: () => + "Root hash of the provided zkProgram config witness is invalid", + + propertyNotMatchingStep: (propertyName: string, step: string) => + `${propertyName} not matching: ${step}`, + + stateRootNotMatching: (step: string) => + errors.propertyNotMatchingStep("StateRoots", step), + + transactionsHashNotMatching: (step: string) => + errors.propertyNotMatchingStep("Transactions hash", step), + + networkStateHashNotMatching: (step: string) => + errors.propertyNotMatchingStep("Network state hash", step), +}; + +type ApplyTransactionArguments = Omit< + TransactionProverTransactionArguments, + "verificationKeyAttestation" +>; + +export class TransactionProverZkProgrammable extends ZkProgrammable< + TransactionProverPublicInput, + TransactionProverPublicOutput +> { + public constructor( + private readonly prover: TransactionProver, + private readonly transactionHooks: ProvableTransactionHook[], + private readonly stateServiceProvider: StateServiceProvider, + private readonly verificationKeyService: MinimalVKTreeService + ) { + super(); + } + + name = "TransactionProver"; + + public get areProofsEnabled(): AreProofsEnabled | undefined { + return this.prover.areProofsEnabled; + } + + /** + * Applies and checks the two proofs and applies the corresponding state + * changes to the given state. + * + * The rough high level workflow of this function: + * 1. Execute beforeTransaction hooks, pushing the ST batch + * 2. Add Transaction to bundle, meaning appending it to all the respective commitments + * 3. Push the runtime ST batch + * 4. Execute afterTransaction hooks, pushing the ST batch + * 5. Some consistency checks and signature verification + * + * @param fromState The from-state of the BlockProver + * @param runtimeOutput + * @param executionData + * @param networkState + * @returns The new BlockProver-state to be used as public output + */ + public async applyTransaction( + fromState: TransactionProverState, + runtimeOutput: MethodPublicOutput, + executionData: ApplyTransactionArguments, + networkState: NetworkState + ): Promise { + const { transaction, signature } = executionData; + + let state = { ...fromState }; + + const { isMessage } = runtimeOutput; + + const beforeTxHookArguments = toBeforeTransactionHookArgument( + executionData, + networkState, + state + ); + + // Apply beforeTransaction hook state transitions + const beforeBatch = await this.executeTransactionHooks( + async (module, args) => await module.beforeTransaction(args), + beforeTxHookArguments, + isMessage + ); + + state = addTransactionToBundle(state, runtimeOutput.isMessage, transaction); + + state.pendingSTBatches.push(beforeBatch); + + state.pendingSTBatches.push({ + batchHash: runtimeOutput.stateTransitionsHash, + applied: runtimeOutput.status, + }); + + // Apply afterTransaction hook state transitions + const afterTxHookArguments = toAfterTransactionHookArgument( + executionData, + networkState, + state, + runtimeOutput + ); + + // Switch to different state set for afterTx hooks + this.stateServiceProvider.popCurrentStateService(); + + const afterBatch = await this.executeTransactionHooks( + async (module, args) => await module.afterTransaction(args), + afterTxHookArguments, + isMessage + ); + state.pendingSTBatches.push(afterBatch); + + // Check transaction integrity against appProof + const blockTransactionHash = transaction.hash(); + + blockTransactionHash.assertEquals( + runtimeOutput.transactionHash, + "Transactions provided in AppProof and BlockProof do not match" + ); + + // Check transaction signature + new SignedTransaction({ + transaction, + signature, + }) + .validateSignature() + .or(isMessage) + .assertTrue("Transaction signature not valid"); + + // Validate layout of transaction witness + transaction.assertTransactionType(isMessage); + + // Check network state integrity against appProof + state.networkState + .hash() + .assertEquals( + runtimeOutput.networkStateHash, + "Network state does not match state used in AppProof" + ); + + return new TransactionProverState(state); + } + + private verifyVerificationKeyAttestation( + attestation: RuntimeVerificationKeyAttestation, + methodId: Field + ): VerificationKey { + // Verify the [methodId, vk] tuple against the baked-in vk tree root + const { verificationKey, witness: verificationKeyTreeWitness } = + attestation; + + const root = Field(this.verificationKeyService.getRoot()); + const calculatedRoot = verificationKeyTreeWitness.calculateRoot( + new MethodVKConfigData({ + methodId: methodId, + vkHash: verificationKey.hash, + }).hash() + ); + root.assertEquals(calculatedRoot, errors.invalidZkProgramTreeRoot()); + + return verificationKey; + } + + private async executeTransactionHooks< + T extends BeforeTransactionHookArguments | AfterTransactionHookArguments, + >( + hook: (module: ProvableTransactionHook, args: T) => Promise, + hookArguments: T, + isMessage: Bool + ) { + const { batch, rawStatus } = await executeHooks( + hookArguments, + async () => { + for (const module of this.transactionHooks) { + // eslint-disable-next-line no-await-in-loop + await hook(module, hookArguments); + } + }, + isMessage + ); + + // This is going to set applied to false in case the hook fails + // (that's only possible for messages though as others are hard-asserted) + batch.applied = rawStatus; + + return batch; + } + + public async proveTransactionInternal( + fromState: TransactionProverState, + runtimeProof: DynamicRuntimeProof, + { transaction, networkState }: BlockProverSingleTransactionExecutionData + ): Promise { + const verificationKey = this.verifyVerificationKeyAttestation( + transaction.verificationKeyAttestation, + transaction.transaction.methodId + ); + + runtimeProof.verify(verificationKey); + + return await this.applyTransaction( + fromState, + runtimeProof.publicOutput, + transaction, + networkState + ); + } + + @provableMethod() + public async proveTransaction( + publicInput: TransactionProverPublicInput, + runtimeProof: DynamicRuntimeProof, + executionData: BlockProverSingleTransactionExecutionData + ): Promise { + const state = TransactionProverState.fromCommitments( + publicInput, + executionData.networkState + ); + + const stateTo = await this.proveTransactionInternal( + state, + runtimeProof, + executionData + ); + + return new TransactionProverPublicOutput(stateTo.toCommitments()); + } + + @provableMethod() + public async proveTransactions( + publicInput: TransactionProverPublicInput, + runtimeProof1: DynamicRuntimeProof, + runtimeProof2: DynamicRuntimeProof, + executionData: BlockProverMultiTransactionExecutionData + ): Promise { + const state = TransactionProverState.fromCommitments( + publicInput, + executionData.networkState + ); + + // this.staticChecks(publicInput); + + const state1 = await this.proveTransactionInternal(state, runtimeProof1, { + transaction: executionData.transaction1, + networkState: executionData.networkState, + }); + + // Switch to next state record for 2nd tx beforeTx hook + // TODO Can be prevented by merging 1st afterTx + 2nd beforeTx + this.stateServiceProvider.popCurrentStateService(); + + const stateTo = await this.proveTransactionInternal(state1, runtimeProof2, { + transaction: executionData.transaction2, + networkState: executionData.networkState, + }); + + return new TransactionProverPublicOutput(stateTo.toCommitments()); + } + + @provableMethod() + public async merge( + publicInput: TransactionProverPublicInput, + proof1: TransactionProof, + proof2: TransactionProof + ): Promise { + proof1.verify(); + proof2.verify(); + + // Check transaction list hash. + // Only assert them if these are tx proofs, skip for closed proofs + publicInput.transactionsHash + .equals(proof1.publicInput.transactionsHash) + .assertTrue( + errors.transactionsHashNotMatching("publicInput.from -> proof1.from") + ); + proof1.publicOutput.transactionsHash + .equals(proof2.publicInput.transactionsHash) + .assertTrue( + errors.transactionsHashNotMatching("proof1.to -> proof2.from") + ); + + // Check networkhash + publicInput.networkStateHash.assertEquals( + proof1.publicInput.networkStateHash, + errors.networkStateHashNotMatching("publicInput.from -> proof1.from") + ); + proof1.publicOutput.networkStateHash.assertEquals( + proof2.publicInput.networkStateHash, + errors.networkStateHashNotMatching("proof1.to -> proof2.from") + ); + + // Check eternalTransactionsHash + publicInput.eternalTransactionsHash.assertEquals( + proof1.publicInput.eternalTransactionsHash, + errors.transactionsHashNotMatching("publicInput.from -> proof1.from") + ); + proof1.publicOutput.eternalTransactionsHash.assertEquals( + proof2.publicInput.eternalTransactionsHash, + errors.transactionsHashNotMatching("proof1.to -> proof2.from") + ); + + // Check incomingMessagesHash + publicInput.incomingMessagesHash.assertEquals( + proof1.publicInput.incomingMessagesHash, + errors.propertyNotMatchingStep( + "IncomingMessagesHash", + "publicInput.from -> proof1.from" + ) + ); + proof1.publicOutput.incomingMessagesHash.assertEquals( + proof2.publicInput.incomingMessagesHash, + errors.propertyNotMatchingStep( + "IncomingMessagesHash", + "proof1.to -> proof2.from" + ) + ); + + // Check pendingSTBatchesHash + publicInput.pendingSTBatchesHash.assertEquals( + proof1.publicInput.pendingSTBatchesHash, + errors.transactionsHashNotMatching("publicInput.from -> proof1.from") + ); + proof1.publicOutput.pendingSTBatchesHash.assertEquals( + proof2.publicInput.pendingSTBatchesHash, + errors.transactionsHashNotMatching("proof1.to -> proof2.from") + ); + + // Check witnessedRootsHash + publicInput.witnessedRootsHash.assertEquals( + proof1.publicInput.witnessedRootsHash, + errors.transactionsHashNotMatching("publicInput.from -> proof1.from") + ); + proof1.publicOutput.witnessedRootsHash.assertEquals( + proof2.publicInput.witnessedRootsHash, + errors.transactionsHashNotMatching("proof1.to -> proof2.from") + ); + + return new TransactionProverPublicOutput({ + transactionsHash: proof2.publicOutput.transactionsHash, + networkStateHash: proof2.publicOutput.networkStateHash, + eternalTransactionsHash: proof2.publicOutput.eternalTransactionsHash, + incomingMessagesHash: proof2.publicOutput.incomingMessagesHash, + pendingSTBatchesHash: proof2.publicOutput.pendingSTBatchesHash, + witnessedRootsHash: proof2.publicOutput.witnessedRootsHash, + }); + } + + /** + * Creates the BlockProver ZkProgram. + * Recursive linking of proofs is done via the previously + * injected StateTransitionProver and the required AppChainProof class + */ + public zkProgramFactory(): PlainZkProgram< + TransactionProverPublicInput, + TransactionProverPublicOutput + >[] { + const { prover } = this; + const proveTransaction = prover.proveTransaction.bind(prover); + const proveTransactions = prover.proveTransactions.bind(prover); + const merge = prover.merge.bind(prover); + + const program = ZkProgram({ + name: "BlockProver", + publicInput: TransactionProverPublicInput, + publicOutput: TransactionProverPublicOutput, + + methods: { + proveTransaction: { + privateInputs: [ + DynamicRuntimeProof, + BlockProverSingleTransactionExecutionData, + ], + + async method( + publicInput: TransactionProverPublicInput, + runtimeProof: DynamicRuntimeProof, + executionData: BlockProverSingleTransactionExecutionData + ) { + return { + publicOutput: await proveTransaction( + publicInput, + runtimeProof, + executionData + ), + }; + }, + }, + + proveTransactions: { + privateInputs: [ + DynamicRuntimeProof, + DynamicRuntimeProof, + BlockProverMultiTransactionExecutionData, + ], + + async method( + publicInput: TransactionProverPublicInput, + runtimeProof1: DynamicRuntimeProof, + runtimeProof2: DynamicRuntimeProof, + executionData: BlockProverMultiTransactionExecutionData + ) { + return { + publicOutput: await proveTransactions( + publicInput, + runtimeProof1, + runtimeProof2, + executionData + ), + }; + }, + }, + + merge: { + privateInputs: [ + SelfProof< + TransactionProverPublicInput, + TransactionProverPublicOutput + >, + SelfProof< + TransactionProverPublicInput, + TransactionProverPublicOutput + >, + ], + + async method( + publicInput: TransactionProverPublicInput, + proof1: TransactionProof, + proof2: TransactionProof + ) { + return { publicOutput: await merge(publicInput, proof1, proof2) }; + }, + }, + }, + }); + + const methods = { + proveTransaction: program.proveTransaction, + proveTransactions: program.proveTransactions, + merge: program.merge, + }; + + const SelfProofClass = ZkProgram.Proof(program); + + return [ + { + name: program.name, + compile: program.compile.bind(program), + verify: program.verify.bind(program), + analyzeMethods: program.analyzeMethods.bind(program), + Proof: SelfProofClass, + methods, + }, + ]; + } +} + +/** + * BlockProver class, which aggregates a AppChainProof and + * a StateTransitionProof into a single BlockProof, that can + * then be merged to be committed to the base-layer contract + */ +@injectable() +export class TransactionProver + extends ProtocolModule + implements TransactionProvable, CompilableModule +{ + public zkProgrammable: TransactionProverZkProgrammable; + + public constructor( + @inject("Runtime") + public readonly runtime: WithZkProgrammable & + CompilableModule, + @injectAll("ProvableTransactionHook") + transactionHooks: ProvableTransactionHook[], + @inject("StateServiceProvider") + stateServiceProvider: StateServiceProvider, + verificationKeyService: RuntimeVerificationKeyRootService + ) { + super(); + this.zkProgrammable = new TransactionProverZkProgrammable( + this, + transactionHooks, + stateServiceProvider, + verificationKeyService + ); + } + + public async compile( + registry: CompileRegistry + ): Promise | undefined> { + return await registry.forceProverExists(async () => { + await this.runtime.compile(registry); + return await this.zkProgrammable.compile(registry); + }); + } + + public proveTransaction( + publicInput: TransactionProverPublicInput, + runtimeProof: DynamicRuntimeProof, + executionData: BlockProverSingleTransactionExecutionData + ): Promise { + return this.zkProgrammable.proveTransaction( + publicInput, + runtimeProof, + executionData + ); + } + + public proveTransactions( + publicInput: TransactionProverPublicInput, + runtimeProof1: DynamicRuntimeProof, + runtimeProof2: DynamicRuntimeProof, + executionData: BlockProverMultiTransactionExecutionData + ): Promise { + return this.zkProgrammable.proveTransactions( + publicInput, + runtimeProof1, + runtimeProof2, + executionData + ); + } + + public merge( + publicInput: TransactionProverPublicInput, + proof1: TransactionProof, + proof2: TransactionProof + ): Promise { + return this.zkProgrammable.merge(publicInput, proof1, proof2); + } +} diff --git a/packages/protocol/src/prover/utils.ts b/packages/protocol/src/prover/utils.ts new file mode 100644 index 000000000..5b67de0f1 --- /dev/null +++ b/packages/protocol/src/prover/utils.ts @@ -0,0 +1,106 @@ +import { Bool } from "o1js"; +import { container } from "tsyringe"; + +import { + ProvableStateTransition, + StateTransition, +} from "../model/StateTransition"; +import { AppliedStateTransitionBatch } from "../model/AppliedStateTransitionBatch"; +import { + RuntimeMethodExecutionContext, + RuntimeMethodExecutionData, +} from "../state/context/RuntimeMethodExecutionContext"; +import { RuntimeTransaction } from "../model/transaction/RuntimeTransaction"; +import { MinaActions } from "../utils/MinaPrefixedProvableHashList"; + +import { StateTransitionReductionList } from "./accumulators/StateTransitionReductionList"; +import { TransactionProverState } from "./transaction/TransactionProvable"; + +/** + * Constructs a AppliedBatch based on a list of STs and the flag whether to + * be applied or not. The AppliedBatch is a condensed commitment to a batch + * of STs. + */ +export function constructBatch( + stateTransitions: StateTransition[], + applied: Bool +) { + const transitions = stateTransitions.map((transition) => + transition.toProvable() + ); + + const hashList = new StateTransitionReductionList(ProvableStateTransition); + transitions.forEach((transition) => { + hashList.push(transition); + }); + + return new AppliedStateTransitionBatch({ + batchHash: hashList.commitment, + applied, + }); +} + +// TODO How does this interact with the RuntimeMethodExecutionContext when executing runtimemethods? +export async function executeHooks( + contextArguments: RuntimeMethodExecutionData, + method: () => Promise, + isMessage: Bool | undefined = undefined +) { + const executionContext = container.resolve(RuntimeMethodExecutionContext); + executionContext.clear(); + + // Setup context for potential calls to runtime methods. + // This way they can use this.transaction etc. while still having provable + // integrity between data + executionContext.setup(contextArguments); + executionContext.beforeMethod("", "", []); + + const result = await method(); + + executionContext.afterMethod(); + + const { stateTransitions, status, statusMessage } = + executionContext.current().result; + + // See https://github.com/proto-kit/framework/issues/321 for why we do this here + if (isMessage !== undefined) { + // isMessage is defined for all tx hooks + status + .or(isMessage) + .assertTrue( + `Transaction hook call failed for non-message tx: ${statusMessage ?? "-"}` + ); + } else { + // isMessage is undefined for all block hooks + status.assertTrue(`Block hook call failed: ${statusMessage ?? "-"}`); + } + + return { + batch: constructBatch(stateTransitions, Bool(true)), + result, + rawStatus: status, + }; +} + +export function addTransactionToBundle< + T extends Pick< + TransactionProverState, + "transactionList" | "eternalTransactionsList" | "incomingMessages" + >, +>(state: T, isMessage: Bool, transaction: RuntimeTransaction): T { + const transactionHash = transaction.hash(); + + // Append tx to transaction list + state.transactionList.pushIf(transactionHash, isMessage.not()); + + // Append tx to eternal transaction list + // TODO Change that to the a sequence-state compatible transaction struct + state.eternalTransactionsList.push(transactionHash); + + // Append tx to incomingMessagesHash + const actionHash = MinaActions.actionHash(transaction.hashData()); + + state.incomingMessages.pushIf(actionHash, isMessage); + + return state; +} diff --git a/packages/protocol/src/settlement/modularity/ProvableSettlementHook.ts b/packages/protocol/src/settlement/modularity/ProvableSettlementHook.ts index 65ccf7a12..67148bfa1 100644 --- a/packages/protocol/src/settlement/modularity/ProvableSettlementHook.ts +++ b/packages/protocol/src/settlement/modularity/ProvableSettlementHook.ts @@ -3,7 +3,7 @@ import { InferProofBase } from "@proto-kit/common"; import { ProtocolModule } from "../../protocol/ProtocolModule"; import { NetworkState } from "../../model/network/NetworkState"; -import type { BlockProof } from "../../prover/block/BlockProver"; +import type { BlockProof } from "../../prover/block/BlockProvable"; import type { SettlementContractType } from "../contracts/settlement/SettlementBase"; export type InputBlockProof = InferProofBase; From d6b341810be0b175aa52eb5db7f575823e5bcec3 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 14:44:49 +0100 Subject: [PATCH 02/10] Separated TransactionProver from BlockProver: Sequencer + Library --- .../src/protocol/VanillaProtocolModules.ts | 2 + .../src/protocol/production/flow/BlockFlow.ts | 23 +++---- .../sequencing/TransactionExecutionService.ts | 9 +-- .../production/tasks/BlockReductionTask.ts | 4 +- .../protocol/production/tasks/NewBlockTask.ts | 9 ++- .../tasks/TransactionProvingTask.ts | 19 ++--- .../tasks/TransactionReductionTask.ts | 69 +++++++++++++++++++ .../NewBlockProvingParametersSerializer.ts | 17 ++--- ...ansactionProvingTaskParameterSerializer.ts | 36 +++++----- .../tracing/TransactionTracingService.ts | 23 ++----- .../sequencer/test/settlement/Settlement.ts | 1 + packages/stack/src/scripts/graphql/server.ts | 1 + 12 files changed, 135 insertions(+), 78 deletions(-) create mode 100644 packages/sequencer/src/protocol/production/tasks/TransactionReductionTask.ts diff --git a/packages/library/src/protocol/VanillaProtocolModules.ts b/packages/library/src/protocol/VanillaProtocolModules.ts index 89a87d08b..81332e714 100644 --- a/packages/library/src/protocol/VanillaProtocolModules.ts +++ b/packages/library/src/protocol/VanillaProtocolModules.ts @@ -6,6 +6,7 @@ import { ProtocolModulesRecord, StateTransitionProver, LastStateRootBlockHook, + TransactionProver, } from "@proto-kit/protocol"; import { PrivateKey } from "o1js"; @@ -21,6 +22,7 @@ export class VanillaProtocolModules { ): MandatoryProtocolModulesRecord & ProtocolModules { return { StateTransitionProver, + TransactionProver, BlockProver, AccountState: AccountStateHook, BlockHeight: BlockHeightHook, diff --git a/packages/sequencer/src/protocol/production/flow/BlockFlow.ts b/packages/sequencer/src/protocol/production/flow/BlockFlow.ts index 8dea8b59c..7f5a9de00 100644 --- a/packages/sequencer/src/protocol/production/flow/BlockFlow.ts +++ b/packages/sequencer/src/protocol/production/flow/BlockFlow.ts @@ -1,19 +1,19 @@ import { inject, injectable, Lifecycle, scoped } from "tsyringe"; import { - BlockProof, BlockProverPublicInput, BlockProverPublicOutput, MandatoryProtocolModulesRecord, Protocol, + TransactionProof, } from "@proto-kit/protocol"; import { Bool, Field } from "o1js"; import { MAX_FIELD } from "@proto-kit/common"; import { TransactionProvingTask } from "../tasks/TransactionProvingTask"; -import { BlockReductionTask } from "../tasks/BlockReductionTask"; import { TransactionProvingTaskParameters } from "../tasks/serializers/types/TransactionProvingTypes"; import { FlowCreator } from "../../../worker/flow/Flow"; import { BlockTrace } from "../tracing/BlockTracingService"; +import { TransactionReductionTask } from "../tasks/TransactionReductionTask"; import { ReductionTaskFlow } from "./ReductionTaskFlow"; import { TransactionFlow } from "./TransactionFlow"; @@ -26,7 +26,7 @@ export class BlockFlow { @inject("Protocol") private readonly protocol: Protocol, private readonly transactionProvingTask: TransactionProvingTask, - private readonly blockReductionTask: BlockReductionTask, + private readonly transactionReductionTask: TransactionReductionTask, private readonly transactionFlow: TransactionFlow ) {} @@ -54,22 +54,19 @@ export class BlockFlow { private async executeTransactions( trace: BlockTrace - ): Promise> { + ): Promise< + ReductionTaskFlow + > { const transactionFlow = new ReductionTaskFlow( { name: `transactions-${trace.height}`, inputLength: trace.transactions.length, mappingTask: this.transactionProvingTask, - reductionTask: this.blockReductionTask, + reductionTask: this.transactionReductionTask, mergableFunction: (a, b) => - a.publicOutput.stateRoot - .equals(b.publicInput.stateRoot) - .and( - a.publicOutput.transactionsHash.equals( - b.publicInput.transactionsHash - ) - ) + a.publicOutput.transactionsHash + .equals(b.publicInput.transactionsHash) .and( a.publicInput.networkStateHash.equals( b.publicInput.networkStateHash @@ -99,7 +96,7 @@ export class BlockFlow { public async executeBlock( trace: BlockTrace, - callback: (proof: BlockProof) => Promise + callback: (proof: TransactionProof) => Promise ) { if (trace.transactions.length === 0) { const proof = await this.dummyTransactionProof(trace); diff --git a/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts b/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts index 753cfbb8f..24dc9852a 100644 --- a/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts +++ b/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts @@ -13,8 +13,6 @@ import { MandatoryProtocolModulesRecord, reduceStateTransitions, StateTransition, - BlockProver, - BlockProverProgrammable, BeforeTransactionHookArguments, AfterTransactionHookArguments, BlockProverState, @@ -23,6 +21,7 @@ import { toAfterTransactionHookArgument, ProvableStateTransition, DefaultProvableHashList, + addTransactionToBundle, } from "@proto-kit/protocol"; import { Bool, Field } from "o1js"; import { AreProofsEnabled, log, mapSequential } from "@proto-kit/common"; @@ -210,8 +209,6 @@ export type TransactionExecutionResultStatus = export class TransactionExecutionService { private readonly transactionHooks: ProvableTransactionHook[]; - private readonly blockProver: BlockProverProgrammable; - private readonly txHooks: ProvableTransactionHook[]; public constructor( @@ -227,8 +224,6 @@ export class TransactionExecutionService { this.transactionHooks = protocol.dependencyContainer.resolveAll( "ProvableTransactionHook" ); - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - this.blockProver = (protocol.blockProver as BlockProver).zkProgrammable; this.txHooks = protocol.dependencyContainer.resolveAll( @@ -320,7 +315,7 @@ export class TransactionExecutionService { ): BlockTrackers { const signedTransaction = tx.toProtocolTransaction(); // Add tx to commitments - return this.blockProver.addTransactionToBundle( + return addTransactionToBundle( state, Bool(tx.isMessage), signedTransaction.transaction diff --git a/packages/sequencer/src/protocol/production/tasks/BlockReductionTask.ts b/packages/sequencer/src/protocol/production/tasks/BlockReductionTask.ts index 068bd3488..24b5423a0 100644 --- a/packages/sequencer/src/protocol/production/tasks/BlockReductionTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/BlockReductionTask.ts @@ -18,7 +18,6 @@ import { PairTuple, ProofTaskSerializer, } from "../../../helpers/utils"; -import { VerificationKeyService } from "../../runtime/RuntimeVerificationKeyService"; @injectable() @scoped(Lifecycle.ContainerScoped) @@ -36,8 +35,7 @@ export class BlockReductionTask MandatoryProtocolModulesRecord & ProtocolModulesRecord >, private readonly executionContext: ProvableMethodExecutionContext, - private readonly compileRegistry: CompileRegistry, - private readonly verificationKeyService: VerificationKeyService + private readonly compileRegistry: CompileRegistry ) { super(); this.blockProver = this.protocol.blockProver; diff --git a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts index 460d0c337..806a5b459 100644 --- a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts @@ -2,7 +2,6 @@ import { inject, injectable, Lifecycle, scoped } from "tsyringe"; import { BlockProvable, BlockProverPublicInput, - BlockProverPublicOutput, NetworkState, Protocol, StateTransitionProof, @@ -10,8 +9,10 @@ import { BlockHashMerkleTreeWitness, MandatoryProtocolModulesRecord, WitnessedRootWitness, + TransactionProof, + BlockProof, } from "@proto-kit/protocol"; -import { Bool, Proof } from "o1js"; +import { Bool } from "o1js"; import { ProvableMethodExecutionContext, CompileRegistry, @@ -26,8 +27,6 @@ import type { TaskStateRecord } from "../tracing/BlockTracingService"; import { NewBlockProvingParametersSerializer } from "./serializers/NewBlockProvingParametersSerializer"; import { executeWithPrefilledStateService } from "./TransactionProvingTask"; -type BlockProof = Proof; - export interface NewBlockProverParameters { publicInput: BlockProverPublicInput; networkState: NetworkState; @@ -40,7 +39,7 @@ export interface NewBlockProverParameters { export type NewBlockProvingParameters = PairingDerivedInput< StateTransitionProof, - BlockProof, + TransactionProof, NewBlockProverParameters >; diff --git a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts index 38473a22c..2de3ac069 100644 --- a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts @@ -1,11 +1,12 @@ import { BlockProof, - BlockProvable, MandatoryProtocolModulesRecord, Protocol, ProtocolModulesRecord, StateServiceProvider, DynamicRuntimeProof, + TransactionProvable, + TransactionProof, } from "@proto-kit/protocol"; import { Runtime } from "@proto-kit/module"; import { inject, injectable, Lifecycle, scoped } from "tsyringe"; @@ -53,9 +54,9 @@ export async function executeWithPrefilledStateService( @scoped(Lifecycle.ContainerScoped) export class TransactionProvingTask extends TaskWorkerModule - implements Task + implements Task { - private readonly blockProver: BlockProvable; + private readonly transactionProver: TransactionProvable; private readonly runtimeProofType = this.runtime.zkProgrammable.zkProgram[0].Proof; @@ -72,7 +73,7 @@ export class TransactionProvingTask private readonly compileRegistry: CompileRegistry ) { super(); - this.blockProver = protocol.blockProver; + this.transactionProver = protocol.transactionProver; } public inputSerializer(): TaskSerializer { @@ -84,9 +85,9 @@ export class TransactionProvingTask ); } - public resultSerializer(): TaskSerializer { + public resultSerializer(): TaskSerializer { return new ProofTaskSerializer( - this.blockProver.zkProgrammable.zkProgram[0].Proof + this.transactionProver.zkProgrammable.zkProgram[0].Proof ); } @@ -102,13 +103,13 @@ export class TransactionProvingTask const proof1 = DynamicRuntimeProof.fromProof(input.proof1); if (type === TransactionProvingType.SINGLE) { - await this.blockProver.proveTransaction( + await this.transactionProver.proveTransaction( parameters.publicInput, proof1, parameters.executionData ); } else { - await this.blockProver.proveTransactions( + await this.transactionProver.proveTransactions( parameters.publicInput, proof1, DynamicRuntimeProof.fromProof(input.proof2), @@ -128,6 +129,6 @@ export class TransactionProvingTask public async prepare(): Promise { // Compile - await this.blockProver.compile(this.compileRegistry); + await this.transactionProver.compile(this.compileRegistry); } } diff --git a/packages/sequencer/src/protocol/production/tasks/TransactionReductionTask.ts b/packages/sequencer/src/protocol/production/tasks/TransactionReductionTask.ts new file mode 100644 index 000000000..46e5ddac9 --- /dev/null +++ b/packages/sequencer/src/protocol/production/tasks/TransactionReductionTask.ts @@ -0,0 +1,69 @@ +import { inject, injectable, Lifecycle, scoped } from "tsyringe"; +import { + MandatoryProtocolModulesRecord, + Protocol, + ProtocolModulesRecord, + TransactionProof, + TransactionProvable, +} from "@proto-kit/protocol"; +import { + CompileRegistry, + ProvableMethodExecutionContext, +} from "@proto-kit/common"; + +import { TaskWorkerModule } from "../../../worker/worker/TaskWorkerModule"; +import { Task, TaskSerializer } from "../../../worker/flow/Task"; +import { + PairProofTaskSerializer, + PairTuple, + ProofTaskSerializer, +} from "../../../helpers/utils"; + +@injectable() +@scoped(Lifecycle.ContainerScoped) +export class TransactionReductionTask + extends TaskWorkerModule + implements Task, TransactionProof> +{ + private readonly transactionProver: TransactionProvable; + + public name = "transactionReduction"; + + public constructor( + @inject("Protocol") + private readonly protocol: Protocol< + MandatoryProtocolModulesRecord & ProtocolModulesRecord + >, + private readonly executionContext: ProvableMethodExecutionContext, + private readonly compileRegistry: CompileRegistry + ) { + super(); + this.transactionProver = this.protocol.transactionProver; + } + + public inputSerializer(): TaskSerializer> { + return new PairProofTaskSerializer( + this.transactionProver.zkProgrammable.zkProgram[0].Proof + ); + } + + public resultSerializer(): TaskSerializer { + return new ProofTaskSerializer( + this.transactionProver.zkProgrammable.zkProgram[0].Proof + ); + } + + public async compute( + input: PairTuple + ): Promise { + const [r1, r2] = input; + await this.transactionProver.merge(r1.publicInput, r1, r2); + return await this.executionContext + .current() + .result.prove(); + } + + public async prepare(): Promise { + await this.transactionProver.compile(this.compileRegistry); + } +} diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts b/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts index c8a1f2640..58771b1df 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts @@ -1,13 +1,14 @@ import { BlockHashMerkleTreeWitness, - BlockProof, BlockProverPublicInput, - BlockProverPublicOutput, NetworkState, ReturnType, StateTransitionProof, StateTransitionProverPublicInput, StateTransitionProverPublicOutput, + TransactionProof, + TransactionProverPublicInput, + TransactionProverPublicOutput, WitnessedRootWitness, } from "@proto-kit/protocol"; import { Bool } from "o1js"; @@ -38,7 +39,7 @@ interface JsonType { type NewBlockPayload = PairingDerivedInput< StateTransitionProof, - BlockProof, + TransactionProof, NewBlockProverParameters >; @@ -50,16 +51,16 @@ export class NewBlockProvingParametersSerializer StateTransitionProverPublicInput, StateTransitionProverPublicOutput >, - private readonly blockProofSerializer: ProofTaskSerializer< - BlockProverPublicInput, - BlockProverPublicOutput + private readonly transactionProofSerializer: ProofTaskSerializer< + TransactionProverPublicInput, + TransactionProverPublicOutput > ) {} public toJSON(input: NewBlockPayload) { return JSON.stringify({ input1: this.stProofSerializer.toJSON(input.input1), - input2: this.blockProofSerializer.toJSON(input.input2), + input2: this.transactionProofSerializer.toJSON(input.input2), params: { publicInput: BlockProverPublicInput.toJSON(input.params.publicInput), @@ -92,7 +93,7 @@ export class NewBlockProvingParametersSerializer const jsonObject: JsonType = JSON.parse(json); return { input1: await this.stProofSerializer.fromJSON(jsonObject.input1), - input2: await this.blockProofSerializer.fromJSON(jsonObject.input2), + input2: await this.transactionProofSerializer.fromJSON(jsonObject.input2), params: { publicInput: BlockProverPublicInput.fromJSON( diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts b/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts index da655d863..3e08b804e 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts @@ -1,10 +1,10 @@ import { BlockProverPublicInput, - BlockProverTransactionArguments, MethodPublicOutput, NetworkState, ReturnType, RuntimeTransaction, + TransactionProverTransactionArguments, } from "@proto-kit/protocol"; import { JsonProof, Signature } from "o1js"; @@ -21,7 +21,7 @@ import { } from "./DecodedStateSerializer"; import { RuntimeVerificationKeyAttestationSerializer } from "./RuntimeVerificationKeyAttestationSerializer"; -export type BlockProverTransactionArgumentsJSON = { +export type TransactionProverTransactionArgumentsJSON = { transaction: ReturnType; signature: ReturnType; verificationKeyAttestation: ReturnType< @@ -30,13 +30,13 @@ export type BlockProverTransactionArgumentsJSON = { }; export type SingleExecutionDataJSON = { - transaction: BlockProverTransactionArgumentsJSON; + transaction: TransactionProverTransactionArgumentsJSON; networkState: ReturnType; }; export type MultiExecutionDataJSON = { - transaction1: BlockProverTransactionArgumentsJSON; - transaction2: BlockProverTransactionArgumentsJSON; + transaction1: TransactionProverTransactionArgumentsJSON; + transaction2: TransactionProverTransactionArgumentsJSON; networkState: ReturnType; }; @@ -71,9 +71,9 @@ export class TransactionProvingTaskParameterSerializer > ) {} - private blockProverArgumentsToJson( - args: BlockProverTransactionArguments - ): BlockProverTransactionArgumentsJSON { + private transactionProverArgumentsToJson( + args: TransactionProverTransactionArguments + ): TransactionProverTransactionArgumentsJSON { return { transaction: RuntimeTransaction.toJSON(args.transaction), // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment @@ -85,9 +85,9 @@ export class TransactionProvingTaskParameterSerializer }; } - private blockProverArgumentsFromJson( - args: BlockProverTransactionArgumentsJSON - ): BlockProverTransactionArguments { + private transactionProverArgumentsFromJson( + args: TransactionProverTransactionArgumentsJSON + ): TransactionProverTransactionArguments { return { transaction: new RuntimeTransaction( RuntimeTransaction.fromJSON(args.transaction) @@ -120,7 +120,9 @@ export class TransactionProvingTaskParameterSerializer const { executionData } = parameters; const executionDataJson: SingleExecutionDataJSON = { networkState: NetworkState.toJSON(executionData.networkState), - transaction: this.blockProverArgumentsToJson(executionData.transaction), + transaction: this.transactionProverArgumentsToJson( + executionData.transaction + ), }; taskParamsJson = { @@ -135,10 +137,10 @@ export class TransactionProvingTaskParameterSerializer const { executionData } = parameters; const executionDataJson: MultiExecutionDataJSON = { networkState: NetworkState.toJSON(executionData.networkState), - transaction1: this.blockProverArgumentsToJson( + transaction1: this.transactionProverArgumentsToJson( executionData.transaction1 ), - transaction2: this.blockProverArgumentsToJson( + transaction2: this.transactionProverArgumentsToJson( executionData.transaction2 ), }; @@ -183,7 +185,7 @@ export class TransactionProvingTaskParameterSerializer parameters: { ...partialParameters, executionData: { - transaction: this.blockProverArgumentsFromJson( + transaction: this.transactionProverArgumentsFromJson( parameters.executionData.transaction ), networkState: new NetworkState( @@ -205,10 +207,10 @@ export class TransactionProvingTaskParameterSerializer parameters: { ...partialParameters, executionData: { - transaction1: this.blockProverArgumentsFromJson( + transaction1: this.transactionProverArgumentsFromJson( parameters.executionData.transaction1 ), - transaction2: this.blockProverArgumentsFromJson( + transaction2: this.transactionProverArgumentsFromJson( parameters.executionData.transaction2 ), networkState: new NetworkState( diff --git a/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts b/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts index 4b30745b5..ee591d282 100644 --- a/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts +++ b/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts @@ -1,18 +1,15 @@ import { - BlockProver, + addTransactionToBundle, BlockProverMultiTransactionExecutionData, - BlockProverProgrammable, BlockProverPublicInput, BlockProverSingleTransactionExecutionData, - BlockProverTransactionArguments, - MandatoryProtocolModulesRecord, NetworkState, - Protocol, + TransactionProverTransactionArguments, } from "@proto-kit/protocol"; import { Bool, Field } from "o1js"; import { MAX_FIELD } from "@proto-kit/common"; import { toStateTransitionsHash } from "@proto-kit/module"; -import { inject, injectable } from "tsyringe"; +import { injectable } from "tsyringe"; import { TransactionExecutionResult } from "../../../storage/model/Block"; import { PendingTransaction } from "../../../mempool/PendingTransaction"; @@ -59,19 +56,13 @@ export function collectStartingState( @injectable() export class TransactionTracingService { - private readonly blockProver: BlockProverProgrammable; - public constructor( - private readonly verificationKeyService: VerificationKeyService, - @inject("Protocol") protocol: Protocol - ) { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - this.blockProver = (protocol.blockProver as BlockProver).zkProgrammable; - } + private readonly verificationKeyService: VerificationKeyService + ) {} public async getTransactionData( transaction: PendingTransaction - ): Promise { + ): Promise { const verificationKeyAttestation = this.verificationKeyService.getAttestation( transaction.methodId.toBigInt() @@ -105,7 +96,7 @@ export class TransactionTracingService { transaction: TransactionExecutionResult ) { // TODO Remove this call and instead reuse results from sequencing - const newState = this.blockProver.addTransactionToBundle( + const newState = addTransactionToBundle( previousState, Bool(transaction.tx.isMessage), transaction.tx.toRuntimeTransaction() diff --git a/packages/sequencer/test/settlement/Settlement.ts b/packages/sequencer/test/settlement/Settlement.ts index f0012e9cc..ec4aeb5db 100644 --- a/packages/sequencer/test/settlement/Settlement.ts +++ b/packages/sequencer/test/settlement/Settlement.ts @@ -198,6 +198,7 @@ export const settlementTestFn = ( }, Protocol: { StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, AccountState: {}, BlockProver: {}, diff --git a/packages/stack/src/scripts/graphql/server.ts b/packages/stack/src/scripts/graphql/server.ts index a927694bf..2cdaf2604 100644 --- a/packages/stack/src/scripts/graphql/server.ts +++ b/packages/stack/src/scripts/graphql/server.ts @@ -137,6 +137,7 @@ export async function startServer() { Protocol: { BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, AccountState: {}, BlockHeight: {}, TransactionFee: { From 2fdbbab25174566cc2014a1ec5128faca3405e6f Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 14:48:35 +0100 Subject: [PATCH 03/10] Separated TransactionProver from BlockProver: Tests --- packages/sequencer/test/integration/BlockProduction-test.ts | 1 + packages/sequencer/test/integration/BlockProductionSize.test.ts | 1 + packages/sequencer/test/integration/Mempool.test.ts | 1 + packages/sequencer/test/integration/Proven.test.ts | 1 + packages/sequencer/test/integration/StorageIntegration.test.ts | 1 + .../production/sequencing/atomic-block-production.test.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/packages/sequencer/test/integration/BlockProduction-test.ts b/packages/sequencer/test/integration/BlockProduction-test.ts index 3189f4798..103e5ff8f 100644 --- a/packages/sequencer/test/integration/BlockProduction-test.ts +++ b/packages/sequencer/test/integration/BlockProduction-test.ts @@ -160,6 +160,7 @@ export function testBlockProduction< AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, ProtocolStateTestHook: {}, diff --git a/packages/sequencer/test/integration/BlockProductionSize.test.ts b/packages/sequencer/test/integration/BlockProductionSize.test.ts index ad263f018..166b26884 100644 --- a/packages/sequencer/test/integration/BlockProductionSize.test.ts +++ b/packages/sequencer/test/integration/BlockProductionSize.test.ts @@ -91,6 +91,7 @@ describe("block limit", () => { AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, ProtocolStateTestHook: {}, diff --git a/packages/sequencer/test/integration/Mempool.test.ts b/packages/sequencer/test/integration/Mempool.test.ts index 2609ef78f..bedb6406a 100644 --- a/packages/sequencer/test/integration/Mempool.test.ts +++ b/packages/sequencer/test/integration/Mempool.test.ts @@ -112,6 +112,7 @@ describe.each([["InMemory", InMemoryDatabase]])( AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, }, diff --git a/packages/sequencer/test/integration/Proven.test.ts b/packages/sequencer/test/integration/Proven.test.ts index 0526d93d2..8d6b48d08 100644 --- a/packages/sequencer/test/integration/Proven.test.ts +++ b/packages/sequencer/test/integration/Proven.test.ts @@ -114,6 +114,7 @@ describe.skip("Proven", () => { AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, ProtocolStateTestHook: {}, diff --git a/packages/sequencer/test/integration/StorageIntegration.test.ts b/packages/sequencer/test/integration/StorageIntegration.test.ts index b26a5008b..d326827db 100644 --- a/packages/sequencer/test/integration/StorageIntegration.test.ts +++ b/packages/sequencer/test/integration/StorageIntegration.test.ts @@ -111,6 +111,7 @@ describe.each([["InMemory", InMemoryDatabase]])( AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, }, diff --git a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts index a9b156023..0212cb622 100644 --- a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts +++ b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts @@ -66,6 +66,7 @@ describe("atomic block production", () => { AccountState: {}, BlockProver: {}, StateTransitionProver: {}, + TransactionProver: {}, BlockHeight: {}, LastStateRoot: {}, ProtocolStateTestHook: {}, From 41edc557e12bdaa1fa8b9f4b434f6abc6a59ebe1 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 15:14:39 +0100 Subject: [PATCH 04/10] Fixed compile and linting errors --- .../src/protocol/VanillaProtocolModules.ts | 20 +++----------- packages/protocol/src/protocol/Protocol.ts | 26 +++++++++++++++++++ .../statetransition/StateTransitionProver.ts | 6 +---- .../test/integration/BlockProduction-test.ts | 7 +---- .../integration/BlockProductionSize.test.ts | 7 +---- .../test/integration/Mempool.test.ts | 14 ++-------- .../sequencer/test/integration/Proven.test.ts | 7 +---- .../integration/StorageIntegration.test.ts | 17 ++---------- .../atomic-block-production.test.ts | 7 +---- .../sequencer/test/settlement/Settlement.ts | 7 +---- packages/stack/src/scripts/worker/app.ts | 1 + 11 files changed, 40 insertions(+), 79 deletions(-) diff --git a/packages/library/src/protocol/VanillaProtocolModules.ts b/packages/library/src/protocol/VanillaProtocolModules.ts index 81332e714..20b936b05 100644 --- a/packages/library/src/protocol/VanillaProtocolModules.ts +++ b/packages/library/src/protocol/VanillaProtocolModules.ts @@ -1,12 +1,7 @@ import { - AccountStateHook, - BlockHeightHook, - BlockProver, MandatoryProtocolModulesRecord, ProtocolModulesRecord, - StateTransitionProver, - LastStateRootBlockHook, - TransactionProver, + Protocol, } from "@proto-kit/protocol"; import { PrivateKey } from "o1js"; @@ -21,12 +16,7 @@ export class VanillaProtocolModules { additionalModules: ProtocolModules ): MandatoryProtocolModulesRecord & ProtocolModules { return { - StateTransitionProver, - TransactionProver, - BlockProver, - AccountState: AccountStateHook, - BlockHeight: BlockHeightHook, - LastStateRoot: LastStateRootBlockHook, + ...Protocol.defaultModules(), ...additionalModules, }; } @@ -42,11 +32,7 @@ export class VanillaProtocolModules { public static mandatoryConfig() { return { - BlockProver: {}, - StateTransitionProver: {}, - AccountState: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), }; } diff --git a/packages/protocol/src/protocol/Protocol.ts b/packages/protocol/src/protocol/Protocol.ts index cbdf4c130..4b0e4d240 100644 --- a/packages/protocol/src/protocol/Protocol.ts +++ b/packages/protocol/src/protocol/Protocol.ts @@ -3,6 +3,7 @@ import { ChildContainerProvider, log, ModuleContainer, + ModulesConfig, ModulesRecord, Startable, StringKeyOf, @@ -22,6 +23,9 @@ import { NoopSettlementHook } from "../hooks/NoopSettlementHook"; import { AccountStateHook } from "../hooks/AccountStateHook"; import { NoopTransactionHook } from "../hooks/NoopTransactionHook"; import { TransactionProvable } from "../prover/transaction/TransactionProvable"; +import { StateTransitionProver } from "../prover/statetransition/StateTransitionProver"; +import { TransactionProver } from "../prover/transaction/TransactionProver"; +import { BlockProver } from "../prover/block/BlockProver"; import { ProtocolModule } from "./ProtocolModule"; import { ProvableTransactionHook } from "./ProvableTransactionHook"; @@ -143,6 +147,28 @@ export class Protocol< >("StateTransitionProver"); } + public static defaultModules() { + return { + StateTransitionProver, + TransactionProver, + BlockProver, + AccountState: AccountStateHook, + BlockHeight: BlockHeightHook, + LastStateRoot: LastStateRootBlockHook, + }; + } + + public static defaultConfig() { + return { + StateTransitionProver: {}, + TransactionProver: {}, + BlockProver: {}, + AccountState: {}, + BlockHeight: {}, + LastStateRoot: {}, + } satisfies ModulesConfig>; + } + public getAreProofsEnabled(): AreProofsEnabled { return this.container.resolve("AreProofsEnabled"); } diff --git a/packages/protocol/src/prover/statetransition/StateTransitionProver.ts b/packages/protocol/src/prover/statetransition/StateTransitionProver.ts index e8716c529..dcfeeaaa7 100644 --- a/packages/protocol/src/prover/statetransition/StateTransitionProver.ts +++ b/packages/protocol/src/prover/statetransition/StateTransitionProver.ts @@ -20,7 +20,6 @@ import { StateTransitionProvableBatch, StateTransitionType, } from "../../model/StateTransitionProvableBatch"; -import { StateTransitionProverType } from "../../protocol/Protocol"; import { ProtocolModule } from "../../protocol/ProtocolModule"; import { DefaultProvableHashList } from "../../utils/ProvableHashList"; import { WitnessedRootHashList } from "../accumulators/WitnessedRootHashList"; @@ -433,10 +432,7 @@ export class StateTransitionProverProgrammable extends ZkProgrammable< @injectable() export class StateTransitionProver extends ProtocolModule - implements - StateTransitionProvable, - StateTransitionProverType, - CompilableModule + implements StateTransitionProvable, CompilableModule { public zkProgrammable: StateTransitionProverProgrammable; diff --git a/packages/sequencer/test/integration/BlockProduction-test.ts b/packages/sequencer/test/integration/BlockProduction-test.ts index 103e5ff8f..71c4172f9 100644 --- a/packages/sequencer/test/integration/BlockProduction-test.ts +++ b/packages/sequencer/test/integration/BlockProduction-test.ts @@ -157,12 +157,7 @@ export function testBlockProduction< EventMaker: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), ProtocolStateTestHook: {}, }, }); diff --git a/packages/sequencer/test/integration/BlockProductionSize.test.ts b/packages/sequencer/test/integration/BlockProductionSize.test.ts index 166b26884..109b576c5 100644 --- a/packages/sequencer/test/integration/BlockProductionSize.test.ts +++ b/packages/sequencer/test/integration/BlockProductionSize.test.ts @@ -88,12 +88,7 @@ describe("block limit", () => { NoopRuntime: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), ProtocolStateTestHook: {}, }, }); diff --git a/packages/sequencer/test/integration/Mempool.test.ts b/packages/sequencer/test/integration/Mempool.test.ts index bedb6406a..d5aec4037 100644 --- a/packages/sequencer/test/integration/Mempool.test.ts +++ b/packages/sequencer/test/integration/Mempool.test.ts @@ -1,5 +1,4 @@ import { log, TypedClass } from "@proto-kit/common"; -import { VanillaProtocolModules } from "@proto-kit/library"; import { Runtime } from "@proto-kit/module"; import { Protocol } from "@proto-kit/protocol"; import { Bool, PrivateKey, UInt64 } from "o1js"; @@ -74,9 +73,7 @@ describe.each([["InMemory", InMemoryDatabase]])( const sequencerClass = Sequencer.from(testingSequencerModules({})); - const protocolClass = Protocol.from( - VanillaProtocolModules.mandatoryModules({}) - ); + const protocolClass = Protocol.from(Protocol.defaultModules()); return AppChain.from({ Sequencer: sequencerClass, @@ -108,14 +105,7 @@ describe.each([["InMemory", InMemoryDatabase]])( TaskQueue: {}, SequencerStartupModule: {}, }, - Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, - }, + Protocol: Protocol.defaultConfig(), }); // Start AppChain diff --git a/packages/sequencer/test/integration/Proven.test.ts b/packages/sequencer/test/integration/Proven.test.ts index 8d6b48d08..bca7f53af 100644 --- a/packages/sequencer/test/integration/Proven.test.ts +++ b/packages/sequencer/test/integration/Proven.test.ts @@ -111,12 +111,7 @@ describe.skip("Proven", () => { Balances: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), ProtocolStateTestHook: {}, SettlementContractModule: { SettlementContract: {}, diff --git a/packages/sequencer/test/integration/StorageIntegration.test.ts b/packages/sequencer/test/integration/StorageIntegration.test.ts index d326827db..16f17bbe0 100644 --- a/packages/sequencer/test/integration/StorageIntegration.test.ts +++ b/packages/sequencer/test/integration/StorageIntegration.test.ts @@ -1,6 +1,5 @@ import "reflect-metadata"; import { expect } from "@jest/globals"; -import { VanillaProtocolModules } from "@proto-kit/library"; import { Protocol } from "@proto-kit/protocol"; import { Runtime } from "@proto-kit/module"; import { Bool, Field, PrivateKey, UInt64 } from "o1js"; @@ -59,9 +58,6 @@ describe.each([["InMemory", InMemoryDatabase]])( let unprovenState: AsyncStateService; let provenState: AsyncStateService; - // let unprovenTreeStore: AsyncMerkleTreeStore; - // let provenTreeStore: AsyncMerkleTreeStore; - const sk = PrivateKey.random(); const pk = sk.toPublicKey(); let pkNonce = 0; @@ -77,9 +73,7 @@ describe.each([["InMemory", InMemoryDatabase]])( Balance, }); - const protocolClass = Protocol.from( - VanillaProtocolModules.mandatoryModules({}) - ); + const protocolClass = Protocol.from(Protocol.defaultModules()); return AppChain.from({ Sequencer: sequencerClass, @@ -107,14 +101,7 @@ describe.each([["InMemory", InMemoryDatabase]])( FeeStrategy: {}, SequencerStartupModule: {}, }, - Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, - }, + Protocol: Protocol.defaultConfig(), }); await appChain.start(false); diff --git a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts index 0212cb622..e34b72af6 100644 --- a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts +++ b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts @@ -63,12 +63,7 @@ describe("atomic block production", () => { Balance: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), ProtocolStateTestHook: {}, }, }); diff --git a/packages/sequencer/test/settlement/Settlement.ts b/packages/sequencer/test/settlement/Settlement.ts index ec4aeb5db..3e1c86f26 100644 --- a/packages/sequencer/test/settlement/Settlement.ts +++ b/packages/sequencer/test/settlement/Settlement.ts @@ -197,12 +197,7 @@ export const settlementTestFn = ( }, }, Protocol: { - StateTransitionProver: {}, - TransactionProver: {}, - BlockHeight: {}, - AccountState: {}, - BlockProver: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), SettlementContractModule: { SettlementContract: {}, BridgeContract: {}, diff --git a/packages/stack/src/scripts/worker/app.ts b/packages/stack/src/scripts/worker/app.ts index 2ae296279..f1964c30d 100644 --- a/packages/stack/src/scripts/worker/app.ts +++ b/packages/stack/src/scripts/worker/app.ts @@ -34,6 +34,7 @@ export const app = { protocol: { AccountState: {}, BlockProver: {}, + TransactionProver: {}, BlockHeight: {}, StateTransitionProver: {}, LastStateRoot: {}, From 9b51fbaf8e5211604896d487a6c90b9cb82b26c8 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 15:45:45 +0100 Subject: [PATCH 05/10] Fixed typing and tests --- .../src/protocol/ProvableBlockHook.ts | 4 +- .../src/prover/block/BlockProvable.ts | 129 ++++++------------ .../protocol/src/prover/block/BlockProver.ts | 9 +- .../prover/transaction/TransactionProver.ts | 3 - packages/protocol/test/TestingProtocol.ts | 26 +--- 5 files changed, 52 insertions(+), 119 deletions(-) diff --git a/packages/protocol/src/protocol/ProvableBlockHook.ts b/packages/protocol/src/protocol/ProvableBlockHook.ts index b15863b92..7ba6dcd32 100644 --- a/packages/protocol/src/protocol/ProvableBlockHook.ts +++ b/packages/protocol/src/protocol/ProvableBlockHook.ts @@ -4,13 +4,13 @@ import { NoConfig } from "@proto-kit/common"; import { NetworkState } from "../model/network/NetworkState"; import { BlockProverState, - BlockProverStateCommitments, + BlockProverPublicInput, } from "../prover/block/BlockProvable"; import { TransitioningProtocolModule } from "./TransitioningProtocolModule"; export type ProvableHookBlockState = Pick< - BlockProverStateCommitments, + BlockProverPublicInput, | "transactionsHash" | "eternalTransactionsHash" | "incomingMessagesHash" diff --git a/packages/protocol/src/prover/block/BlockProvable.ts b/packages/protocol/src/prover/block/BlockProvable.ts index c7f7299ba..dbcb311fe 100644 --- a/packages/protocol/src/prover/block/BlockProvable.ts +++ b/packages/protocol/src/prover/block/BlockProvable.ts @@ -10,122 +10,81 @@ import { WitnessedRootHashList, WitnessedRootWitness, } from "../accumulators/WitnessedRootHashList"; -import { TransactionProof } from "../transaction/TransactionProvable"; +import { + TransactionProof, + TransactionProverState, + TransactionProverStateCommitments, +} from "../transaction/TransactionProvable"; import { BlockHashMerkleTreeWitness } from "./accummulators/BlockHashMerkleTree"; -// Should be equal to BlockProver.PublicInput -export interface BlockProverState { +export class BlockProverState extends TransactionProverState { /** * The current state root of the block prover */ stateRoot: Field; - /** - * The current commitment of the transaction-list which - * will at the end equal the bundle hash - */ - transactionList: TransactionHashList; - - /** - * The network state which gives access to values such as blockHeight - * This value is the same for the whole batch (L2 block) - */ - networkState: NetworkState; - /** * The root of the merkle tree encoding all block hashes, * see `BlockHashMerkleTree` */ blockHashRoot: Field; - /** - * A variant of the transactionsHash that is never reset. - * Thought for usage in the sequence state mempool. - * In comparison, transactionsHash restarts at 0 for every new block - */ - eternalTransactionsList: TransactionHashList; - - pendingSTBatches: AppliedBatchHashList; - - incomingMessages: MinaActionsHashList; - - witnessedRoots: WitnessedRootHashList; - blockNumber: Field; -} -// TODO Sort and organize public inputs and outputs -export class BlockProverStateCommitments extends Struct({ - transactionsHash: Field, - stateRoot: Field, - // Commitment to the list of unprocessed (pending) batches of STs that need to be proven - pendingSTBatchesHash: Field, - witnessedRootsHash: Field, - networkStateHash: Field, - blockHashRoot: Field, - eternalTransactionsHash: Field, - incomingMessagesHash: Field, - blockNumber: Field, -}) { - public static fromBlockProverState( - state: BlockProverState - ): BlockProverStateCommitments { + constructor(args: { + transactionList: TransactionHashList; + networkState: NetworkState; + eternalTransactionsList: TransactionHashList; + pendingSTBatches: AppliedBatchHashList; + incomingMessages: MinaActionsHashList; + witnessedRoots: WitnessedRootHashList; + stateRoot: Field; + blockHashRoot: Field; + blockNumber: Field; + }) { + super(args); + this.stateRoot = args.stateRoot; + this.blockHashRoot = args.blockHashRoot; + this.blockNumber = args.blockNumber; + } + + public toCommitments(): BlockProverPublicInput { return { - networkStateHash: state.networkState.hash(), - stateRoot: state.stateRoot, - blockNumber: state.blockNumber, - blockHashRoot: state.blockHashRoot, - pendingSTBatchesHash: state.pendingSTBatches.commitment, - transactionsHash: state.transactionList.commitment, - eternalTransactionsHash: state.eternalTransactionsList.commitment, - incomingMessagesHash: state.incomingMessages.commitment, - witnessedRootsHash: state.witnessedRoots.commitment, + ...super.toCommitments(), + stateRoot: this.stateRoot, + blockHashRoot: this.blockHashRoot, + blockNumber: this.blockNumber, }; } - public static toBlockProverState( - publicInput: BlockProverStateCommitments, + public static fromCommitments( + publicInput: BlockProverPublicInput, networkState: NetworkState ): BlockProverState { - publicInput.networkStateHash.assertEquals( - networkState.hash(), - "ExecutionData Networkstate doesn't equal public input hash" - ); - - return { - networkState, + return new BlockProverState({ + ...super.fromCommitments(publicInput, networkState), stateRoot: publicInput.stateRoot, blockHashRoot: publicInput.blockHashRoot, - transactionList: new TransactionHashList(publicInput.transactionsHash), - eternalTransactionsList: new TransactionHashList( - publicInput.eternalTransactionsHash - ), - incomingMessages: new MinaActionsHashList( - publicInput.incomingMessagesHash - ), - pendingSTBatches: new AppliedBatchHashList( - publicInput.pendingSTBatchesHash - ), - witnessedRoots: new WitnessedRootHashList(publicInput.witnessedRootsHash), blockNumber: publicInput.blockNumber, - }; + }); } } -export class BlockProverPublicInput extends BlockProverStateCommitments {} - -export class BlockProverPublicOutput extends Struct({ - transactionsHash: Field, +export const BlockProverStateCommitments = { + ...TransactionProverStateCommitments, stateRoot: Field, - pendingSTBatchesHash: Field, - witnessedRootsHash: Field, - networkStateHash: Field, blockHashRoot: Field, - eternalTransactionsHash: Field, - incomingMessagesHash: Field, - closed: Bool, blockNumber: Field, +}; + +export class BlockProverPublicInput extends Struct( + BlockProverStateCommitments +) {} + +export class BlockProverPublicOutput extends Struct({ + ...BlockProverStateCommitments, + closed: Bool, }) { public equals(input: BlockProverPublicInput, closed: Bool): Bool { const output2 = BlockProverPublicOutput.toFields({ diff --git a/packages/protocol/src/prover/block/BlockProver.ts b/packages/protocol/src/prover/block/BlockProver.ts index 6b3d1e02d..42bfbae40 100644 --- a/packages/protocol/src/prover/block/BlockProver.ts +++ b/packages/protocol/src/prover/block/BlockProver.ts @@ -44,7 +44,7 @@ import { BlockProof, BlockProverPublicInput, BlockProverPublicOutput, - BlockProverStateCommitments, + BlockProverState, } from "./BlockProvable"; import { BlockHashMerkleTreeWitness, @@ -232,10 +232,7 @@ export class BlockProverProgrammable extends ZkProgrammable< "TransactionProof cannot alter the network state" ); - const state = BlockProverStateCommitments.toBlockProverState( - publicInput, - networkState - ); + const state = BlockProverState.fromCommitments(publicInput, networkState); // Verify Transaction proof if it has at least 1 tx - i.e. the // input and output doesn't match fully @@ -376,7 +373,7 @@ export class BlockProverProgrammable extends ZkProgrammable< state.blockNumber = blockIndex.add(1); return new BlockProverPublicOutput({ - ...BlockProverStateCommitments.fromBlockProverState(state), + ...state.toCommitments(), closed: Bool(true), }); } diff --git a/packages/protocol/src/prover/transaction/TransactionProver.ts b/packages/protocol/src/prover/transaction/TransactionProver.ts index 9294edda8..6c686ba17 100644 --- a/packages/protocol/src/prover/transaction/TransactionProver.ts +++ b/packages/protocol/src/prover/transaction/TransactionProver.ts @@ -50,9 +50,6 @@ const errors = { propertyNotMatchingStep: (propertyName: string, step: string) => `${propertyName} not matching: ${step}`, - stateRootNotMatching: (step: string) => - errors.propertyNotMatchingStep("StateRoots", step), - transactionsHashNotMatching: (step: string) => errors.propertyNotMatchingStep("Transactions hash", step), diff --git a/packages/protocol/test/TestingProtocol.ts b/packages/protocol/test/TestingProtocol.ts index 6370201e8..9e8d85fa2 100644 --- a/packages/protocol/test/TestingProtocol.ts +++ b/packages/protocol/test/TestingProtocol.ts @@ -3,33 +3,13 @@ import { Runtime } from "@proto-kit/module"; import { Balance } from "@proto-kit/sequencer/test/integration/mocks/Balance"; import { NoopRuntime } from "@proto-kit/sequencer/test/integration/mocks/NoopRuntime"; -import { - AccountStateHook, - BlockHeightHook, - BlockProver, - LastStateRootBlockHook, - Protocol, - StateServiceProvider, - StateTransitionProver, -} from "../src"; +import { Protocol, StateServiceProvider } from "../src"; export function createAndInitTestingProtocol() { - const ProtocolClass = Protocol.from({ - StateTransitionProver: StateTransitionProver, - BlockProver: BlockProver, - AccountState: AccountStateHook, - BlockHeight: BlockHeightHook, - LastStateRoot: LastStateRootBlockHook, - }); + const ProtocolClass = Protocol.from(Protocol.defaultModules()); const protocol = new ProtocolClass(); - protocol.configure({ - BlockProver: {}, - AccountState: {}, - BlockHeight: {}, - StateTransitionProver: {}, - LastStateRoot: {}, - }); + protocol.configure(Protocol.defaultConfig()); const appChain = container.createChildContainer(); From 8415723ba023e3ae18249f4551d21ca167adb954 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 19 Dec 2025 15:56:58 +0100 Subject: [PATCH 06/10] Fixed BlockProver compilation order --- .../protocol/src/prover/block/BlockProver.ts | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/protocol/src/prover/block/BlockProver.ts b/packages/protocol/src/prover/block/BlockProver.ts index 42bfbae40..ede4ea8c9 100644 --- a/packages/protocol/src/prover/block/BlockProver.ts +++ b/packages/protocol/src/prover/block/BlockProver.ts @@ -13,7 +13,6 @@ import { ZkProgrammable, } from "@proto-kit/common"; -import { MethodPublicOutput } from "../../model/MethodPublicOutput"; import { ProtocolModule } from "../../protocol/ProtocolModule"; import { StateTransitionProof, @@ -36,7 +35,9 @@ import { StateServiceProvider } from "../../state/StateServiceProvider"; import { executeHooks } from "../utils"; import { TransactionProof, + TransactionProvable, TransactionProverPublicInput, + TransactionProverPublicOutput, } from "../transaction/TransactionProvable"; import { @@ -77,6 +78,10 @@ export class BlockProverProgrammable extends ZkProgrammable< StateTransitionProverPublicInput, StateTransitionProverPublicOutput >, + public readonly transactionProver: ZkProgrammable< + TransactionProverPublicInput, + TransactionProverPublicOutput + >, private readonly blockHooks: ProvableBlockHook[], private readonly stateServiceProvider: StateServiceProvider ) { @@ -547,8 +552,9 @@ export class BlockProverProgrammable extends ZkProgrammable< BlockProverPublicInput, BlockProverPublicOutput >[] { - const { prover, stateTransitionProver } = this; + const { prover, stateTransitionProver, transactionProver } = this; const StateTransitionProofClass = stateTransitionProver.zkProgram[0].Proof; + const TransactionProofClass = transactionProver.zkProgram[0].Proof; const proveBlock = prover.proveBlock.bind(prover); const merge = prover.merge.bind(prover); @@ -565,7 +571,7 @@ export class BlockProverProgrammable extends ZkProgrammable< StateTransitionProofClass, Bool, WitnessedRootWitness, - SelfProof, + TransactionProofClass, ], async method( publicInput: BlockProverPublicInput, @@ -646,9 +652,12 @@ export class BlockProver StateTransitionProverPublicOutput > & StateTransitionProvable, - @inject("Runtime") - public readonly runtime: WithZkProgrammable & - CompilableModule, + @inject("TransactionProver") + public readonly transactionProver: WithZkProgrammable< + TransactionProverPublicInput, + TransactionProverPublicOutput + > & + TransactionProvable, @injectAll("ProvableBlockHook") blockHooks: ProvableBlockHook[], @inject("StateServiceProvider") @@ -658,6 +667,7 @@ export class BlockProver this.zkProgrammable = new BlockProverProgrammable( this, stateTransitionProver.zkProgrammable, + transactionProver.zkProgrammable, blockHooks, stateServiceProvider ); @@ -668,7 +678,7 @@ export class BlockProver ): Promise | undefined> { return await registry.forceProverExists(async () => { await this.stateTransitionProver.compile(registry); - await this.runtime.compile(registry); + await this.transactionProver.compile(registry); return await this.zkProgrammable.compile(registry); }); } From bae4575771a0268e50809d11e9f88fdd719c1bb9 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 20 Dec 2025 13:21:45 +0100 Subject: [PATCH 07/10] Fixed tests and missing task in definition --- packages/sdk/src/testing/TestingAppChain.ts | 6 +- .../src/protocol/production/flow/BatchFlow.ts | 3 + .../src/protocol/production/flow/BlockFlow.ts | 16 ++--- .../protocol/production/tasks/NewBlockTask.ts | 14 ++-- .../tasks/TransactionProvingTask.ts | 7 +- packages/sequencer/src/worker/flow/Flow.ts | 72 ++++++++++--------- .../worker/worker/LocalTaskWorkerModule.ts | 3 + 7 files changed, 64 insertions(+), 57 deletions(-) diff --git a/packages/sdk/src/testing/TestingAppChain.ts b/packages/sdk/src/testing/TestingAppChain.ts index 63e703715..3487a4175 100644 --- a/packages/sdk/src/testing/TestingAppChain.ts +++ b/packages/sdk/src/testing/TestingAppChain.ts @@ -53,11 +53,7 @@ export class TestingAppChain< appChain.configurePartial({ Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), TransactionFee: { tokenId: 0n, feeRecipient: randomFeeRecipient, diff --git a/packages/sequencer/src/protocol/production/flow/BatchFlow.ts b/packages/sequencer/src/protocol/production/flow/BatchFlow.ts index 6fc80b17e..63720c4fc 100644 --- a/packages/sequencer/src/protocol/production/flow/BatchFlow.ts +++ b/packages/sequencer/src/protocol/production/flow/BatchFlow.ts @@ -124,6 +124,9 @@ export class BatchFlow { map[index].input1 = dummySTProof; }); + // TODO Make sure we use deferErrorsTo to everywhere (preferably with a nice pattern) + // Currently, a lot of errors just get eaten and the chain just halts with no + // error being thrown await this.stateTransitionFlow.executeBatches( batch.stateTransitionTrace, batchId, diff --git a/packages/sequencer/src/protocol/production/flow/BlockFlow.ts b/packages/sequencer/src/protocol/production/flow/BlockFlow.ts index 7f5a9de00..542bd1ca3 100644 --- a/packages/sequencer/src/protocol/production/flow/BlockFlow.ts +++ b/packages/sequencer/src/protocol/production/flow/BlockFlow.ts @@ -1,13 +1,12 @@ import { inject, injectable, Lifecycle, scoped } from "tsyringe"; import { - BlockProverPublicInput, - BlockProverPublicOutput, MandatoryProtocolModulesRecord, Protocol, TransactionProof, + TransactionProverPublicInput, + TransactionProverPublicOutput, } from "@proto-kit/protocol"; -import { Bool, Field } from "o1js"; -import { MAX_FIELD } from "@proto-kit/common"; +import { Field } from "o1js"; import { TransactionProvingTask } from "../tasks/TransactionProvingTask"; import { TransactionProvingTaskParameters } from "../tasks/serializers/types/TransactionProvingTypes"; @@ -35,17 +34,14 @@ export class BlockFlow { ...trace.blockParams.publicInput, networkStateHash: Field(0), transactionsHash: Field(0), - blockHashRoot: Field(0), - blockNumber: MAX_FIELD, - } satisfies BlockProverPublicInput; + } satisfies TransactionProverPublicInput; // TODO Set publicInput.stateRoot to result after block hooks! - const publicOutput = new BlockProverPublicOutput({ + const publicOutput = new TransactionProverPublicOutput({ ...publicInput, - closed: Bool(true), }); - return await this.protocol.blockProver.zkProgrammable.zkProgram[0].Proof.dummy( + return await this.protocol.transactionProver.zkProgrammable.zkProgram[0].Proof.dummy( publicInput, publicOutput, 2 diff --git a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts index 806a5b459..338ce9514 100644 --- a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts @@ -11,6 +11,7 @@ import { WitnessedRootWitness, TransactionProof, BlockProof, + TransactionProvable, } from "@proto-kit/protocol"; import { Bool } from "o1js"; import { @@ -51,6 +52,8 @@ export class NewBlockTask { private readonly stateTransitionProver: StateTransitionProvable; + private readonly transactionProver: TransactionProvable; + private readonly blockProver: BlockProvable; public readonly name = "newBlock"; @@ -63,7 +66,8 @@ export class NewBlockTask ) { super(); this.stateTransitionProver = protocol.stateTransitionProver; - this.blockProver = this.protocol.blockProver; + this.transactionProver = protocol.transactionProver; + this.blockProver = protocol.blockProver; } public inputSerializer(): TaskSerializer { @@ -71,13 +75,13 @@ export class NewBlockTask this.stateTransitionProver.zkProgrammable.zkProgram[0].Proof ); - const blockProofSerializer = new ProofTaskSerializer( - this.blockProver.zkProgrammable.zkProgram[0].Proof + const transactionProofSerializer = new ProofTaskSerializer( + this.transactionProver.zkProgrammable.zkProgram[0].Proof ); return new NewBlockProvingParametersSerializer( stProofSerializer, - blockProofSerializer + transactionProofSerializer ); } @@ -125,6 +129,6 @@ export class NewBlockTask public async prepare(): Promise { // Compile - await this.blockProver.compile(this.compileRegistry); + await this.transactionProver.compile(this.compileRegistry); } } diff --git a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts index 2de3ac069..de4b80b63 100644 --- a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts @@ -1,5 +1,4 @@ import { - BlockProof, MandatoryProtocolModulesRecord, Protocol, ProtocolModulesRecord, @@ -61,7 +60,7 @@ export class TransactionProvingTask private readonly runtimeProofType = this.runtime.zkProgrammable.zkProgram[0].Proof; - public name = "block"; + public name = "transaction"; public constructor( @inject("Protocol") @@ -93,7 +92,7 @@ export class TransactionProvingTask public async compute( input: TransactionProvingTaskParameters - ): Promise { + ): Promise { await executeWithPrefilledStateService( this.protocol.stateServiceProvider, input.parameters.startingState, @@ -123,7 +122,7 @@ export class TransactionProvingTask this.protocol.stateServiceProvider, input.parameters.startingState, async () => - await this.executionContext.current().result.prove() + await this.executionContext.current().result.prove() ); } diff --git a/packages/sequencer/src/worker/flow/Flow.ts b/packages/sequencer/src/worker/flow/Flow.ts index bd9dd7fd0..6dd7d5e02 100644 --- a/packages/sequencer/src/worker/flow/Flow.ts +++ b/packages/sequencer/src/worker/flow/Flow.ts @@ -114,40 +114,46 @@ export class Flow implements Closeable { taskName?: string; } ): Promise { - const queueName = task.name; - const taskName = overrides?.taskName ?? task.name; - const queue = await this.queueImpl.getQueue(queueName); - - const payload = await task.inputSerializer().toJSON(input); - - this.taskCounter += 1; - const taskId = String(this.taskCounter); - - log.trace(`Pushing task ${task.name}`); - - await queue.addTask({ - name: taskName, - taskId, - flowId: this.flowId, - payload, - sequencerId: this.sequencerId, - }); + // We wrap this in a try-catch here, because the flow architecture + // sometimes lets errors vanish + try { + const queueName = task.name; + const taskName = overrides?.taskName ?? task.name; + const queue = await this.queueImpl.getQueue(queueName); + + const payload = await task.inputSerializer().toJSON(input); + this.taskCounter += 1; + const taskId = String(this.taskCounter); + + log.trace(`Pushing task ${task.name}`); + + await queue.addTask({ + name: taskName, + taskId, + flowId: this.flowId, + payload, + sequencerId: this.sequencerId, + }); - this.tasksInProgress += 1; - - const callback = async (returnPayload: TaskPayload) => { - log.trace( - `Completed ${returnPayload.name}, task: ${returnPayload.flowId}:${ - returnPayload?.taskId ?? "-" - }` - ); - const decoded = await task - .resultSerializer() - .fromJSON(returnPayload.payload); - this.tasksInProgress -= 1; - return await completed?.(decoded, input); - }; - await this.waitForResult(queue, taskId, callback); + this.tasksInProgress += 1; + + const callback = async (returnPayload: TaskPayload) => { + log.trace( + `Completed ${returnPayload.name}, task: ${returnPayload.flowId}:${ + returnPayload?.taskId ?? "-" + }` + ); + const decoded = await task + .resultSerializer() + .fromJSON(returnPayload.payload); + this.tasksInProgress -= 1; + return await completed?.(decoded, input); + }; + await this.waitForResult(queue, taskId, callback); + } catch (e) { + log.error(e); + throw e; + } } public async forEach( diff --git a/packages/sequencer/src/worker/worker/LocalTaskWorkerModule.ts b/packages/sequencer/src/worker/worker/LocalTaskWorkerModule.ts index 97aec991a..85fa05663 100644 --- a/packages/sequencer/src/worker/worker/LocalTaskWorkerModule.ts +++ b/packages/sequencer/src/worker/worker/LocalTaskWorkerModule.ts @@ -28,6 +28,7 @@ import { closeable } from "../../sequencer/builder/Closeable"; import { StateTransitionReductionTask } from "../../protocol/production/tasks/StateTransitionReductionTask"; import { TransactionProvingTask } from "../../protocol/production/tasks/TransactionProvingTask"; import { BlockReductionTask } from "../../protocol/production/tasks/BlockReductionTask"; +import { TransactionReductionTask } from "../../protocol/production/tasks/TransactionReductionTask"; import { FlowTaskWorker } from "./FlowTaskWorker"; import { TaskWorkerModule } from "./TaskWorkerModule"; @@ -135,6 +136,7 @@ export class VanillaTaskWorkerModules { StateTransitionReductionTask, RuntimeProvingTask, TransactionProvingTask, + TransactionReductionTask, BlockReductionTask, NewBlockTask, CircuitCompilerTask, @@ -154,6 +156,7 @@ export class VanillaTaskWorkerModules { StateTransitionTask: {}, RuntimeProvingTask: {}, TransactionProvingTask: {}, + TransactionReductionTask: {}, BlockReductionTask: {}, NewBlockTask: {}, StateTransitionReductionTask: {}, From 33e3028e4387f1753a5b2076d9db8757d5d1d839 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sun, 21 Dec 2025 14:05:42 +0100 Subject: [PATCH 08/10] Fixed config for settlement-only test --- packages/sequencer/test/settlement/Settlement-only.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/sequencer/test/settlement/Settlement-only.ts b/packages/sequencer/test/settlement/Settlement-only.ts index 6fe5d8dad..c894bfbcf 100644 --- a/packages/sequencer/test/settlement/Settlement-only.ts +++ b/packages/sequencer/test/settlement/Settlement-only.ts @@ -133,11 +133,7 @@ export const settlementOnlyTestFn = ( }, }, Protocol: { - StateTransitionProver: {}, - BlockHeight: {}, - AccountState: {}, - BlockProver: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), SettlementContractModule: { SettlementContract: {}, }, From b824a5b8d2ea736a15de4a8dcf3c0c8c7dc02877 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sun, 21 Dec 2025 14:19:21 +0100 Subject: [PATCH 09/10] Fixed transaction prover tests again --- packages/sdk/test/blockProof/blockProof.test.ts | 1 - packages/sdk/test/modularization.test.ts | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/sdk/test/blockProof/blockProof.test.ts b/packages/sdk/test/blockProof/blockProof.test.ts index f2c81ad29..2bc920780 100644 --- a/packages/sdk/test/blockProof/blockProof.test.ts +++ b/packages/sdk/test/blockProof/blockProof.test.ts @@ -101,7 +101,6 @@ describe.skip("blockProof", () => { incomingMessagesHash: Field(0), transactionsHash: Field(0), eternalTransactionsHash: Field(0), - blockHashRoot: Field(0), }, }); }); diff --git a/packages/sdk/test/modularization.test.ts b/packages/sdk/test/modularization.test.ts index 307082a71..f0ee7a8bb 100644 --- a/packages/sdk/test/modularization.test.ts +++ b/packages/sdk/test/modularization.test.ts @@ -67,11 +67,7 @@ describe("modularization", () => { TestRuntimeModule: {}, }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - BlockHeight: {}, - LastStateRoot: {}, + ...Protocol.defaultConfig(), TransactionFee: { tokenId: 0n, feeRecipient: PrivateKey.random().toPublicKey().toBase58(), From 71554dbda0b2693cec6b8cd8b6f785a6f9f62015 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sun, 21 Dec 2025 15:52:36 +0100 Subject: [PATCH 10/10] Fixed transaction prover tests again again --- packages/stack/test/graphql/graphql-server.ts | 6 +----- packages/stack/test/graphql/graphql.test.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/stack/test/graphql/graphql-server.ts b/packages/stack/test/graphql/graphql-server.ts index 01c26e565..584fb6fb2 100644 --- a/packages/stack/test/graphql/graphql-server.ts +++ b/packages/stack/test/graphql/graphql-server.ts @@ -135,10 +135,7 @@ export async function startGraphqlServer() { }, Protocol: { - BlockProver: {}, - StateTransitionProver: {}, - AccountState: {}, - BlockHeight: {}, + ...Protocol.defaultConfig(), TransactionFee: { tokenId: 0n, feeRecipient: PrivateKey.random().toPublicKey().toBase58(), @@ -146,7 +143,6 @@ export async function startGraphqlServer() { methods: {}, perWeightUnitFee: 0n, }, - LastStateRoot: {}, }, Sequencer: { diff --git a/packages/stack/test/graphql/graphql.test.ts b/packages/stack/test/graphql/graphql.test.ts index 2cbb2526f..45b29206b 100644 --- a/packages/stack/test/graphql/graphql.test.ts +++ b/packages/stack/test/graphql/graphql.test.ts @@ -57,10 +57,7 @@ function prepareClient() { }, Protocol: { - AccountState: {}, - BlockProver: {}, - StateTransitionProver: {}, - BlockHeight: {}, + ...Protocol.defaultConfig(), TransactionFee: { tokenId: 0n, feeRecipient: PrivateKey.random().toPublicKey().toBase58(), @@ -68,7 +65,6 @@ function prepareClient() { methods: {}, perWeightUnitFee: 0n, }, - LastStateRoot: {}, }, Sequencer: {