diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index fffb566f7950..032741b4be83 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -543,7 +543,9 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP this.utilityExecutor, this.aztecNode, this.contractStore, + this.noteStore, this.anchorBlockHeader, + this.jobId, ); const targetArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata( diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index 042b97de5642..56bf3b207b72 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -363,16 +363,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra this.log.getBindings(), ); - const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId); - - // It is acceptable to run the following operations in parallel for several reasons: - // 1. syncTaggedLogs does not write to the note store — it only stores the pending tagged logs in a capsule array, - // which is then processed in Noir after this handler returns. - // 2. Even if syncTaggedLogs did write to the note store, it would not cause inconsistent state. - await Promise.all([ - logService.syncTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes), - noteService.syncNoteNullifiers(this.contractAddress), - ]); + await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes); } /** diff --git a/yarn-project/pxe/src/contract_sync/index.ts b/yarn-project/pxe/src/contract_sync/index.ts index 7fb3b0a4ee91..4d325d129e21 100644 --- a/yarn-project/pxe/src/contract_sync/index.ts +++ b/yarn-project/pxe/src/contract_sync/index.ts @@ -6,7 +6,9 @@ import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from ' import type { AztecNode } from '@aztec/stdlib/interfaces/client'; import type { BlockHeader } from '@aztec/stdlib/tx'; +import { NoteService } from '../notes/note_service.js'; import type { ContractStore } from '../storage/contract_store/contract_store.js'; +import type { NoteStore } from '../storage/note_store/note_store.js'; /** * Read the current class id of a contract from the execution data provider or AztecNode. If not found, class id @@ -41,6 +43,10 @@ export async function syncState( contractStore: ContractStore, functionToInvokeAfterSync: FunctionSelector | null, utilityExecutor: (privateSyncCall: FunctionCall) => Promise, + noteStore: NoteStore, + aztecNode: AztecNode, + header: BlockHeader, + jobId: string, ) { // Protocol contracts don't have private state to sync if (!isProtocolContract(contractAddress)) { @@ -51,7 +57,11 @@ export async function syncState( ); } - return utilityExecutor(syncStateFunctionCall); + const noteService = new NoteService(noteStore, aztecNode, header, jobId); + + // Both sync_state and syncNoteNullifiers interact with the note store, but running them in parallel is safe + // because note store is designed to handle concurrent operations. + await Promise.all([utilityExecutor(syncStateFunctionCall), noteService.syncNoteNullifiers(contractAddress)]); } } @@ -89,10 +99,21 @@ export async function ensureContractSynced( utilityExecutor: (call: FunctionCall) => Promise, aztecNode: AztecNode, contractStore: ContractStore, + noteStore: NoteStore, header: BlockHeader, + jobId: string, ): Promise { await Promise.all([ - syncState(contractAddress, contractStore, functionToInvokeAfterSync, utilityExecutor), + syncState( + contractAddress, + contractStore, + functionToInvokeAfterSync, + utilityExecutor, + noteStore, + aztecNode, + header, + jobId, + ), verifyCurrentClassId(contractAddress, aztecNode, contractStore, header), ]); } diff --git a/yarn-project/pxe/src/logs/log_service.ts b/yarn-project/pxe/src/logs/log_service.ts index 267a53b004e1..74e15533a487 100644 --- a/yarn-project/pxe/src/logs/log_service.ts +++ b/yarn-project/pxe/src/logs/log_service.ts @@ -108,12 +108,12 @@ export class LogService { ); } - public async syncTaggedLogs( + public async fetchTaggedLogs( contractAddress: AztecAddress, pendingTaggedLogArrayBaseSlot: Fr, scopes?: AztecAddress[], ) { - this.log.verbose('Searching for tagged logs', { contract: contractAddress }); + this.log.verbose(`Fetching tagged logs for ${contractAddress.toString()}`); // We only load logs from block up to and including the anchor block number const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber(); diff --git a/yarn-project/pxe/src/pxe.ts b/yarn-project/pxe/src/pxe.ts index 2d938ac72512..bbde19fe6814 100644 --- a/yarn-project/pxe/src/pxe.ts +++ b/yarn-project/pxe/src/pxe.ts @@ -303,7 +303,9 @@ export class PXE { privateSyncCall => this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId), this.node, this.contractStore, + this.noteStore, anchorBlockHeader, + jobId, ); const result = await contractFunctionSimulator.run( @@ -976,7 +978,9 @@ export class PXE { privateSyncCall => this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId), this.node, this.contractStore, + this.noteStore, anchorBlockHeader, + jobId, ); const executionResult = await this.#simulateUtility( @@ -1047,7 +1051,9 @@ export class PXE { await this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId), this.node, this.contractStore, + this.noteStore, anchorBlockHeader, + jobId, ); }); 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 4074150e067c..23064c6f8685 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 @@ -302,7 +302,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl await this.executeUtilityCall(call); }; - await syncState(targetContractAddress, this.contractStore, functionSelector, utilityExecutor); + const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader(); + await syncState( + targetContractAddress, + this.contractStore, + functionSelector, + utilityExecutor, + this.noteStore, + this.stateMachine.node, + blockHeader, + this.jobId, + ); const blockNumber = await this.txeGetNextBlockNumber(); @@ -314,8 +324,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl const txContext = new TxContext(this.chainId, this.version, gasSettings); - const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader(); - const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber)); const noteCache = new ExecutionNoteCache(protocolNullifier); // In production, the account contract sets the min revertible counter before calling the app function. @@ -663,9 +671,19 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl } // Sync notes before executing utility function to discover notes from previous transactions - await syncState(targetContractAddress, this.contractStore, functionSelector, async call => { - await this.executeUtilityCall(call); - }); + const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader(); + await syncState( + targetContractAddress, + this.contractStore, + functionSelector, + async call => { + await this.executeUtilityCall(call); + }, + this.noteStore, + this.stateMachine.node, + blockHeader, + this.jobId, + ); const call = new FunctionCall( artifact.name,