From ef07d49f9ce304ee9b11ce6744364c0491f0dce6 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 20 Feb 2026 11:04:06 +0000 Subject: [PATCH 1/2] fix(txe): committing after txs --- yarn-project/txe/src/oracle/interfaces.ts | 3 +++ .../oracle/txe_oracle_top_level_context.ts | 19 +++++++++++++++++++ yarn-project/txe/src/rpc_translator.ts | 13 +++++++++++++ yarn-project/txe/src/txe_session.ts | 15 +++++++++++++-- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index 6eb466cd8d80..a253291a138d 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -83,4 +83,7 @@ export interface ITxeExecutionOracle { calldata: Fr[], isStaticCall: boolean, ): Promise; + // TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from + // RPCTranslator::txeGetPrivateEvents and never from Noir. + txeSyncContract(contractAddress: AztecAddress, scope: AztecAddress): Promise; } diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index ca94b79c2c8d..e2bbee6511a6 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -172,6 +172,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers }; } + async txeSyncContract(contractAddress: AztecAddress, scope: AztecAddress) { + if (contractAddress.equals(DEFAULT_ADDRESS)) { + this.logger.debug(`Skipping sync in txeGetPrivateEvents because the events correspond to the default address.`); + return; + } + + const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader(); + await this.stateMachine.contractSyncService.ensureContractSynced( + contractAddress, + null, + async (call, execScopes) => { + await this.executeUtilityCall(call, execScopes); + }, + blockHeader, + this.jobId, + [scope], + ); + } + async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) { return ( await this.privateEventStore.getPrivateEvents(selector, { diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index b81be2164ca9..0a000bc396ba 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -285,6 +285,13 @@ export class RPCTranslator { const contractAddress = addressFromSingle(foreignContractAddress); const scope = addressFromSingle(foreignScope); + // TODO(F-335): Avoid doing the following 2 calls here. + { + await this.handlerAsTxe().txeSyncContract(contractAddress, scope); + // We cycle job to commit the stores after the contract sync. + await this.stateHandler.cycleJob(); + } + const events = await this.handlerAsTxe().txeGetPrivateEvents(selector, contractAddress, scope); if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) { @@ -1040,6 +1047,8 @@ export class RPCTranslator { isStaticCall, ); + // TODO(F-335): Avoid doing the following call here. + await this.stateHandler.cycleJob(); return toForeignCallResult([toArray(returnValues)]); } @@ -1058,6 +1067,8 @@ export class RPCTranslator { args, ); + // TODO(F-335): Avoid doing the following call here. + await this.stateHandler.cycleJob(); return toForeignCallResult([toArray(returnValues)]); } @@ -1074,6 +1085,8 @@ export class RPCTranslator { const returnValues = await this.handlerAsTxe().txePublicCallNewFlow(from, address, calldata, isStaticCall); + // TODO(F-335): Avoid doing the following call here. + await this.stateHandler.cycleJob(); return toForeignCallResult([toArray(returnValues)]); } diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 157f10bb1983..1931e4857cc5 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -113,6 +113,11 @@ export interface TXESessionStateHandler { enterPublicState(contractAddress?: AztecAddress): Promise; enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise; enterUtilityState(contractAddress?: AztecAddress): Promise; + /** + * Commits the current job and begins a new one. Returns the new job ID. + * TODO(F-335): Drop this? + */ + cycleJob(): Promise; } /** @@ -254,6 +259,13 @@ export class TXESession implements TXESessionStateHandler { } } + /** Commits the current job and begins a new one. Returns the new job ID. */ + async cycleJob(): Promise { + await this.jobCoordinator.commitJob(this.currentJobId); + this.currentJobId = this.jobCoordinator.beginJob(); + return this.currentJobId; + } + async enterTopLevelState() { switch (this.state.name) { case 'PRIVATE': { @@ -277,8 +289,7 @@ export class TXESession implements TXESessionStateHandler { } // Commit all staged stores from the job that was just completed, then begin a new job - await this.jobCoordinator.commitJob(this.currentJobId); - this.currentJobId = this.jobCoordinator.beginJob(); + await this.cycleJob(); this.oracleHandler = new TXEOracleTopLevelContext( this.stateMachine, From 19dfe12a3315f477e48c1cc563cb8b42c2307125 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 20 Feb 2026 12:06:40 +0000 Subject: [PATCH 2/2] wip --- yarn-project/txe/src/oracle/interfaces.ts | 4 ++- .../oracle/txe_oracle_top_level_context.ts | 31 +++++++++++-------- yarn-project/txe/src/rpc_translator.ts | 4 ++- yarn-project/txe/src/txe_session.ts | 13 ++++---- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/yarn-project/txe/src/oracle/interfaces.ts b/yarn-project/txe/src/oracle/interfaces.ts index a253291a138d..98dd9f4c0086 100644 --- a/yarn-project/txe/src/oracle/interfaces.ts +++ b/yarn-project/txe/src/oracle/interfaces.ts @@ -71,11 +71,13 @@ export interface ITxeExecutionOracle { args: Fr[], argsHash: Fr, isStaticCall: boolean, + jobId: string, ): Promise; txeExecuteUtilityFunction( targetContractAddress: AztecAddress, functionSelector: FunctionSelector, args: Fr[], + jobId: string, ): Promise; txePublicCallNewFlow( from: AztecAddress, @@ -85,5 +87,5 @@ export interface ITxeExecutionOracle { ): Promise; // TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from // RPCTranslator::txeGetPrivateEvents and never from Noir. - txeSyncContract(contractAddress: AztecAddress, scope: AztecAddress): Promise; + syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string): Promise; } diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index e2bbee6511a6..45be8bbcf95c 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -107,7 +107,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl private senderAddressBookStore: SenderAddressBookStore, private capsuleStore: CapsuleStore, private privateEventStore: PrivateEventStore, - private jobId: string, private nextBlockTimestamp: bigint, private version: Fr, private chainId: Fr, @@ -172,7 +171,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers }; } - async txeSyncContract(contractAddress: AztecAddress, scope: AztecAddress) { + async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) { if (contractAddress.equals(DEFAULT_ADDRESS)) { this.logger.debug(`Skipping sync in txeGetPrivateEvents because the events correspond to the default address.`); return; @@ -183,10 +182,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl contractAddress, null, async (call, execScopes) => { - await this.executeUtilityCall(call, execScopes); + await this.executeUtilityCall(call, execScopes, jobId); }, blockHeader, - this.jobId, + jobId, [scope], ); } @@ -304,6 +303,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl args: Fr[], argsHash: Fr = Fr.zero(), isStaticCall: boolean = false, + jobId: string, ) { this.logger.verbose( `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`, @@ -323,7 +323,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl // Sync notes before executing private function to discover notes from previous transactions const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => { - await this.executeUtilityCall(call, execScopes); + await this.executeUtilityCall(call, execScopes, jobId); }; const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader(); @@ -332,7 +332,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl functionSelector, utilityExecutor, blockHeader, - this.jobId, + jobId, effectiveScopes, ); @@ -379,7 +379,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, contractSyncService: this.stateMachine.contractSyncService, - jobId: this.jobId, + jobId, totalPublicCalldataCount: 0, sideEffectCounter: minRevertibleSideEffectCounter, scopes: effectiveScopes, @@ -678,7 +678,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl return returnValues ?? []; } - async txeExecuteUtilityFunction(targetContractAddress: AztecAddress, functionSelector: FunctionSelector, args: Fr[]) { + async txeExecuteUtilityFunction( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + args: Fr[], + jobId: string, + ) { const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector); if (!artifact) { throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`); @@ -690,10 +695,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl targetContractAddress, functionSelector, async (call, execScopes) => { - await this.executeUtilityCall(call, execScopes); + await this.executeUtilityCall(call, execScopes, jobId); }, blockHeader, - this.jobId, + jobId, 'ALL_SCOPES', ); @@ -708,10 +713,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl returnTypes: [], }); - return this.executeUtilityCall(call, 'ALL_SCOPES'); + return this.executeUtilityCall(call, 'ALL_SCOPES', jobId); } - private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes): Promise { + private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise { const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector); if (entryPointArtifact.functionType !== FunctionType.UTILITY) { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`); @@ -738,7 +743,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, - jobId: this.jobId, + jobId, scopes, }); const acirExecutionResult = await new WASMSimulator() diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 0a000bc396ba..95995654f675 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -287,7 +287,7 @@ export class RPCTranslator { // TODO(F-335): Avoid doing the following 2 calls here. { - await this.handlerAsTxe().txeSyncContract(contractAddress, scope); + await this.handlerAsTxe().syncContractNonOracleMethod(contractAddress, scope, this.stateHandler.getCurrentJob()); // We cycle job to commit the stores after the contract sync. await this.stateHandler.cycleJob(); } @@ -1045,6 +1045,7 @@ export class RPCTranslator { args, argsHash, isStaticCall, + this.stateHandler.getCurrentJob(), ); // TODO(F-335): Avoid doing the following call here. @@ -1065,6 +1066,7 @@ export class RPCTranslator { targetContractAddress, functionSelector, args, + this.stateHandler.getCurrentJob(), ); // TODO(F-335): Avoid doing the following call here. diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 1931e4857cc5..5c7b87ea4feb 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -113,11 +113,10 @@ export interface TXESessionStateHandler { enterPublicState(contractAddress?: AztecAddress): Promise; enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise; enterUtilityState(contractAddress?: AztecAddress): Promise; - /** - * Commits the current job and begins a new one. Returns the new job ID. - * TODO(F-335): Drop this? - */ + + // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions. cycleJob(): Promise; + getCurrentJob(): string; } /** @@ -198,7 +197,6 @@ export class TXESession implements TXESessionStateHandler { senderAddressBookStore, capsuleStore, privateEventStore, - initialJobId, nextBlockTimestamp, version, chainId, @@ -259,6 +257,10 @@ export class TXESession implements TXESessionStateHandler { } } + getCurrentJob(): string { + return this.currentJobId; + } + /** Commits the current job and begins a new one. Returns the new job ID. */ async cycleJob(): Promise { await this.jobCoordinator.commitJob(this.currentJobId); @@ -303,7 +305,6 @@ export class TXESession implements TXESessionStateHandler { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, - this.currentJobId, this.nextBlockTimestamp, this.version, this.chainId,