diff --git a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts index 7520a21f6a88..3b17d54f1553 100644 --- a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts +++ b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts @@ -123,6 +123,7 @@ export class ContractFunctionSimulator { * @param senderForTags - The address that is used as a tagging sender when emitting private logs. Returned from * the `privateGetSenderForTags` oracle. * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. + * @param jobId - The job ID for staged writes. * @returns The result of the execution. */ public async run( @@ -131,13 +132,14 @@ export class ContractFunctionSimulator { selector: FunctionSelector, msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), anchorBlockHeader: BlockHeader, - senderForTags?: AztecAddress, - scopes?: AztecAddress[], + senderForTags: AztecAddress | undefined, + scopes: AztecAddress[] | undefined, + jobId: string, ): Promise { const simulatorSetupTimer = new Timer(); await this.contractStore.syncPrivateState(contractAddress, selector, privateSyncCall => - this.runUtility(privateSyncCall, [], anchorBlockHeader, scopes), + this.runUtility(privateSyncCall, [], anchorBlockHeader, scopes, jobId), ); await verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader); @@ -174,7 +176,7 @@ export class ContractFunctionSimulator { callContext, anchorBlockHeader, async call => { - await this.runUtility(call, [], anchorBlockHeader, scopes); + await this.runUtility(call, [], anchorBlockHeader, scopes, jobId); }, request.authWitnesses, request.capsules, @@ -192,6 +194,7 @@ export class ContractFunctionSimulator { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + jobId, 0, // totalPublicArgsCount startSideEffectCounter, undefined, // log @@ -261,7 +264,8 @@ export class ContractFunctionSimulator { call: FunctionCall, authwits: AuthWitness[], anchorBlockHeader: BlockHeader, - scopes?: AztecAddress[], + scopes: AztecAddress[] | undefined, + jobId: string, ): Promise { await verifyCurrentClassId(call.to, this.aztecNode, this.contractStore, anchorBlockHeader); @@ -286,6 +290,7 @@ export class ContractFunctionSimulator { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + jobId, undefined, scopes, ); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts index e7a8404efb9d..22e28b8be010 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts @@ -140,7 +140,16 @@ describe('Oracle Version Check test suite', () => { // Call the private function with arbitrary message sender and sender for tags const msgSender = await AztecAddress.random(); const senderForTags = await AztecAddress.random(); - await acirSimulator.run(txRequest, contractAddress, selector, msgSender, anchorBlockHeader, senderForTags); + await acirSimulator.run( + txRequest, + contractAddress, + selector, + msgSender, + anchorBlockHeader, + senderForTags, + undefined, + 'test', + ); expect(utilityAssertCompatibleOracleVersionSpy).toHaveBeenCalledTimes(1); }, 30_000); @@ -169,7 +178,7 @@ describe('Oracle Version Check test suite', () => { }; // Call the utility function - await acirSimulator.runUtility(execRequest, [], anchorBlockHeader, []); + await acirSimulator.runUtility(execRequest, [], anchorBlockHeader, [], 'test'); expect(utilityAssertCompatibleOracleVersionSpy).toHaveBeenCalledTimes(1); }, 30_000); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts index 846afe6c7b5c..74a803c3196c 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts @@ -144,6 +144,8 @@ describe('Private Execution test suite', () => { let recipientIvskM: GrumpkinScalar; let senderForTagsIvskM: GrumpkinScalar; + const TEST_JOB_ID = 'test-job-id'; + const treeHeights: { [name: string]: number } = { noteHash: NOTE_HASH_TREE_HEIGHT, l1ToL2Messages: L1_TO_L2_MSG_TREE_HEIGHT, @@ -211,7 +213,16 @@ describe('Private Execution test suite', () => { salt: Fr.random(), }); - return acirSimulator.run(txRequest, contractAddress, selector, msgSender, anchorBlockHeader, senderForTags); + return acirSimulator.run( + txRequest, + contractAddress, + selector, + msgSender, + anchorBlockHeader, + senderForTags, + undefined, + TEST_JOB_ID, + ); }; const insertLeaves = async (leaves: Fr[], name = 'noteHash') => { 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 9b9438049700..41a20efa141f 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 @@ -104,6 +104,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP senderAddressBookStore: SenderAddressBookStore, capsuleStore: CapsuleStore, privateEventStore: PrivateEventStore, + jobId: string, private totalPublicCalldataCount: number = 0, protected sideEffectCounter: number = 0, log = createLogger('simulator:client_execution_context'), @@ -126,6 +127,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP senderAddressBookStore, capsuleStore, privateEventStore, + jobId, log, scopes, ); @@ -586,6 +588,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + this.jobId, this.totalPublicCalldataCount, sideEffectCounter, this.log, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts index 3a3714156158..37a05247b735 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts @@ -190,7 +190,7 @@ describe('Utility Execution test suite', () => { returnTypes: artifact.returnTypes, }; - const result = await acirSimulator.runUtility(execRequest, [], anchorBlockHeader, []); + const result = await acirSimulator.runUtility(execRequest, [], anchorBlockHeader, [], 'test-job-id'); expect(result).toEqual([new Fr(9)]); }, 30_000); @@ -222,6 +222,7 @@ describe('Utility Execution test suite', () => { senderAddressBookStore, capsuleStore, privateEventStore, + 'test-job-id', ); }); 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 987961b68eb6..757ebef994be 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 @@ -64,6 +64,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra protected readonly senderAddressBookStore: SenderAddressBookStore, protected readonly capsuleStore: CapsuleStore, protected readonly privateEventStore: PrivateEventStore, + protected readonly jobId: string, protected log = createLogger('simulator:client_view_context'), protected readonly scopes?: AztecAddress[], ) {} diff --git a/yarn-project/pxe/src/job_coordinator/job_coordinator.test.ts b/yarn-project/pxe/src/job_coordinator/job_coordinator.test.ts new file mode 100644 index 000000000000..aa52728ee98a --- /dev/null +++ b/yarn-project/pxe/src/job_coordinator/job_coordinator.test.ts @@ -0,0 +1,113 @@ +import type { AztecAsyncKVStore } from '@aztec/kv-store'; +import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; + +import { jest } from '@jest/globals'; + +import { JobCoordinator, type StagedStore } from './job_coordinator.js'; + +describe('JobCoordinator', () => { + let store: AztecAsyncKVStore; + let coordinator: JobCoordinator; + + beforeEach(async () => { + store = await openTmpStore('job_coordinator_test'); + coordinator = new JobCoordinator(store); + }); + + describe('beginJob', () => { + it('creates a new job id', () => { + const jobId = coordinator.beginJob(); + + expect(typeof jobId).toBe('string'); + expect(jobId.length).toBeGreaterThan(0); + }); + + // Note: we could eventually be relax this if we want more concurrency, + // but it's good to start with this guardrail + it('throws if job already in progress', () => { + coordinator.beginJob(); + expect(() => coordinator.beginJob()).toThrow(/already in progress/); + }); + + it('tracks job in progress', () => { + coordinator.beginJob(); + expect(coordinator.hasJobInProgress()).toBe(true); + }); + }); + + describe('commitJob', () => { + it('clears job marker on commit', async () => { + const jobId = coordinator.beginJob(); + await coordinator.commitJob(jobId); + expect(coordinator.hasJobInProgress()).toBe(false); + }); + + it('throws if no matching job in progress', async () => { + const jobId = coordinator.beginJob(); + await coordinator.commitJob(jobId); + await expect(coordinator.commitJob(jobId)).rejects.toThrow(/no matching job/); + }); + + it('calls commit on registered stores', async () => { + const commitMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const discardStagedMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const mockStore: StagedStore = { + storeName: 'mock_store', + commit: commitMock, + discardStaged: discardStagedMock, + }; + + coordinator.registerStore(mockStore); + + const jobId = coordinator.beginJob(); + + await coordinator.commitJob(jobId); + + expect(commitMock).toHaveBeenCalledWith(jobId); + }); + }); + + describe('abortJob', () => { + it('clears job marker on abort', async () => { + const jobId = coordinator.beginJob(); + + await coordinator.abortJob(jobId); + + expect(coordinator.hasJobInProgress()).toBe(false); + }); + + it('calls discardStaged on all registered stores', async () => { + const commitMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const discardStagedMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const mockStore: StagedStore = { + storeName: 'mock_store', + commit: commitMock, + discardStaged: discardStagedMock, + }; + + coordinator.registerStore(mockStore); + + const jobId = coordinator.beginJob(); + + await coordinator.abortJob(jobId); + + expect(discardStagedMock).toHaveBeenCalledWith(jobId); + }); + }); + + describe('registerStore', () => { + it('throws on duplicate registration', () => { + const commitMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const discardStagedMock = jest.fn<() => Promise>().mockResolvedValue(undefined); + const mockStore: StagedStore = { + storeName: 'mock_store', + commit: commitMock, + discardStaged: discardStagedMock, + }; + + coordinator.registerStore(mockStore); + + expect(() => coordinator.registerStore(mockStore)).toThrow(/already registered/); + }); + }); +}); diff --git a/yarn-project/pxe/src/job_coordinator/job_coordinator.ts b/yarn-project/pxe/src/job_coordinator/job_coordinator.ts new file mode 100644 index 000000000000..c12739e3c310 --- /dev/null +++ b/yarn-project/pxe/src/job_coordinator/job_coordinator.ts @@ -0,0 +1,149 @@ +import { randomBytes } from '@aztec/foundation/crypto/random'; +import { createLogger } from '@aztec/foundation/log'; +import type { AztecAsyncKVStore } from '@aztec/kv-store'; + +/** + * Interface that stores must implement to support staged writes. + */ +export interface StagedStore { + /** Unique name identifying this store (used for tracking staged stores from JobCoordinator) */ + readonly storeName: string; + + /** + * Commits staged data to main storage. + * Should be called within a transaction for atomicity. + * + * @param jobId - The job identifier + */ + commit(jobId: string): Promise; + + /** + * Discards staged data without committing. + * Called on abort. + * + * @param jobId - The job identifier + */ + discardStaged(jobId: string): Promise; +} + +/** + * JobCoordinator manages job lifecycle and provides crash resilience for PXE operations. + * + * It uses a staged writes pattern: + * 1. When a job begins, a unique job ID is created + * 2. During the job, all writes go to staging (keyed by job ID) + * 3. On commit, staging is promoted to main storage + * 4. On abort, staged data is discarded + * + * Note: PXE should only rely on a single JobCoordinator instance, so it can eventually + * orchestrate concurrent jobs. Right now it doesn't make a difference because we're + * using a job queue with concurrency=1. + */ +export class JobCoordinator { + private readonly log = createLogger('pxe:job_coordinator'); + + /** The underlying KV store */ + kvStore: AztecAsyncKVStore; + + #currentJobId: string | undefined; + #stores: Map = new Map(); + + constructor(kvStore: AztecAsyncKVStore) { + this.kvStore = kvStore; + } + + /** + * Registers a staged store. + * Must be called during initialization for all stores that need staging support. + */ + registerStore(store: StagedStore): void { + if (this.#stores.has(store.storeName)) { + throw new Error(`Store "${store.storeName}" is already registered`); + } + this.#stores.set(store.storeName, store); + this.log.debug(`Registered staged store: ${store.storeName}`); + } + + /** + * Registers multiple staged stores. + */ + registerStores(stores: StagedStore[]): void { + for (const store of stores) { + this.registerStore(store); + } + } + + /** + * Begins a new job and returns a job ID for staged writes. + * + * @returns Job ID to pass to store operations + */ + beginJob(): string { + if (this.#currentJobId) { + throw new Error( + `Cannot begin job: job ${this.#currentJobId} is already in progress. ` + + `This should not happen - ensure jobs are properly committed or aborted.`, + ); + } + + const jobId = randomBytes(8).toString('hex'); + this.#currentJobId = jobId; + + this.log.debug(`Started job ${jobId}`); + return jobId; + } + + /** + * Commits a job by promoting all staged data to main storage. + * + * @param jobId - The job ID returned from beginJob + */ + async commitJob(jobId: string): Promise { + if (!this.#currentJobId || this.#currentJobId !== jobId) { + throw new Error( + `Cannot commit job ${jobId}: no matching job in progress. ` + `Current job: ${this.#currentJobId ?? 'none'}`, + ); + } + + this.log.debug(`Committing job ${jobId}`); + + // Commit all stores atomically in a single transaction. + // Each store's commit is a no-op if it has no staged data (but that's up to each store to handle). + await this.kvStore.transactionAsync(async () => { + for (const store of this.#stores.values()) { + await store.commit(jobId); + } + }); + + this.#currentJobId = undefined; + this.log.debug(`Job ${jobId} committed successfully`); + } + + /** + * Aborts a job by discarding all staged data. + * + * @param jobId - The job ID returned from beginJob + */ + async abortJob(jobId: string): Promise { + if (!this.#currentJobId || this.#currentJobId !== jobId) { + // Job may have already been aborted or never started properly + this.log.warn(`Abort called for job ${jobId} but current job is ${this.#currentJobId ?? 'none'}`); + } + + this.log.debug(`Aborting job ${jobId}`); + + for (const store of this.#stores.values()) { + await store.discardStaged(jobId); + } + + this.#currentJobId = undefined; + this.log.debug(`Job ${jobId} aborted`); + } + + /** + * Checks if there's a job currently in progress. + */ + hasJobInProgress(): boolean { + return this.#currentJobId !== undefined; + } +} diff --git a/yarn-project/pxe/src/pxe.ts b/yarn-project/pxe/src/pxe.ts index e37cb30095eb..5b6c21f0300d 100644 --- a/yarn-project/pxe/src/pxe.ts +++ b/yarn-project/pxe/src/pxe.ts @@ -64,6 +64,7 @@ import { ProxiedNodeFactory } from './contract_function_simulator/proxied_node.j import { PXEDebugUtils } from './debug/pxe_debug_utils.js'; import { enrichPublicSimulationError, enrichSimulationError } from './error_enriching.js'; import { PrivateEventFilterValidator } from './events/private_event_filter_validator.js'; +import { JobCoordinator } from './job_coordinator/job_coordinator.js'; import { PrivateKernelExecutionProver, type PrivateKernelExecutionProverConfig, @@ -108,6 +109,7 @@ export class PXE { private protocolContractsProvider: ProtocolContractsProvider, private log: Logger, private jobQueue: SerialQueue, + private jobCoordinator: JobCoordinator, public debug: PXEDebugUtils, ) {} @@ -154,6 +156,8 @@ export class PXE { loggerOrSuffix, ); + const jobCoordinator = new JobCoordinator(store); + const debugUtils = new PXEDebugUtils(contractStore, noteStore); const jobQueue = new SerialQueue(); @@ -177,6 +181,7 @@ export class PXE { protocolContractsProvider, log, jobQueue, + jobCoordinator, debugUtils, ); @@ -231,7 +236,7 @@ export class PXE { * * Useful for tasks that cannot run concurrently, such as contract function simulation. */ - #putInJobQueue(fn: () => Promise): Promise { + #putInJobQueue(fn: (jobId: string) => Promise): Promise { // TODO(#12636): relax the conditions under which we forbid concurrency. if (this.jobQueue.length() != 0) { this.log.warn( @@ -239,7 +244,22 @@ export class PXE { ); } - return this.jobQueue.put(fn); + return this.jobQueue.put(async () => { + const jobId = this.jobCoordinator.beginJob(); + this.log.verbose(`Beginning job ${jobId}`); + + try { + const result = await fn(jobId); + this.log.verbose(`Committing job ${jobId}`); + + await this.jobCoordinator.commitJob(jobId); + return result; + } catch (err) { + this.log.verbose(`Aborting job ${jobId}`); + await this.jobCoordinator.abortJob(jobId); + throw err; + } + }); } async #registerProtocolContracts() { @@ -272,7 +292,8 @@ export class PXE { async #executePrivate( contractFunctionSimulator: ContractFunctionSimulator, txRequest: TxExecutionRequest, - scopes?: AztecAddress[], + scopes: AztecAddress[] | undefined, + jobId: string, ): Promise { const { origin: contractAddress, functionSelector } = txRequest; @@ -289,6 +310,7 @@ export class PXE { // contract entrypoint undefined, // senderForTags scopes, + jobId, ); this.log.debug(`Private simulation completed for ${contractAddress.toString()}:${functionSelector}`); return result; @@ -307,17 +329,19 @@ export class PXE { * @param authWitnesses - Authentication witnesses required for the function call. * @param scopes - Optional array of account addresses whose notes can be accessed in this call. Defaults to all * accounts if not specified. + * @param jobId - The job ID for staged writes. * @returns The simulation result containing the outputs of the utility function. */ async #simulateUtility( contractFunctionSimulator: ContractFunctionSimulator, call: FunctionCall, - authWitnesses?: AuthWitness[], - scopes?: AztecAddress[], + authWitnesses: AuthWitness[] | undefined, + scopes: AztecAddress[] | undefined, + jobId: string, ) { try { const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader(); - return contractFunctionSimulator.runUtility(call, authWitnesses ?? [], anchorBlockHeader, scopes); + return contractFunctionSimulator.runUtility(call, authWitnesses ?? [], anchorBlockHeader, scopes, jobId); } catch (err) { if (err instanceof SimulationError) { await enrichSimulationError(err, this.contractStore, this.log); @@ -666,14 +690,14 @@ export class PXE { let privateExecutionResult: PrivateExecutionResult; // We disable proving concurrently mostly out of caution, since it accesses some of our stores. Proving is so // computationally demanding that it'd be rare for someone to try to do it concurrently regardless. - return this.#putInJobQueue(async () => { + return this.#putInJobQueue(async jobId => { const totalTimer = new Timer(); try { const syncTimer = new Timer(); await this.blockStateSynchronizer.sync(); const syncTime = syncTimer.ms(); const contractFunctionSimulator = this.#getSimulatorForTx(); - privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest); + privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest, undefined, jobId); const { publicInputs, @@ -750,7 +774,7 @@ export class PXE { skipProofGeneration: boolean = true, ): Promise { // We disable concurrent profiles for consistency with simulateTx. - return this.#putInJobQueue(async () => { + return this.#putInJobQueue(async jobId => { const totalTimer = new Timer(); try { const txInfo = { @@ -770,7 +794,12 @@ export class PXE { const syncTime = syncTimer.ms(); const contractFunctionSimulator = this.#getSimulatorForTx(); - const privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest); + const privateExecutionResult = await this.#executePrivate( + contractFunctionSimulator, + txRequest, + undefined, + jobId, + ); const { executionSteps, timings: { proving } = {} } = await this.#prove( txRequest, @@ -850,7 +879,7 @@ export class PXE { // We disable concurrent simulations since those might execute oracles which read and write to the PXE stores (e.g. // to the capsules), and we need to prevent concurrent runs from interfering with one another (e.g. attempting to // delete the same read value, or reading values that another simulation is currently modifying). - return this.#putInJobQueue(async () => { + return this.#putInJobQueue(async jobId => { try { const totalTimer = new Timer(); const txInfo = { @@ -876,7 +905,7 @@ export class PXE { const skipKernels = overrides?.contracts !== undefined && Object.keys(overrides.contracts ?? {}).length > 0; // Execution of private functions only; no proving, and no kernel logic. - const privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest, scopes); + const privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest, scopes, jobId); let publicInputs: PrivateKernelTailCircuitPublicInputs | undefined; let executionSteps: PrivateExecutionStep[] = []; @@ -991,7 +1020,7 @@ export class PXE { // We disable concurrent simulations since those might execute oracles which read and write to the PXE stores (e.g. // to the capsules), and we need to prevent concurrent runs from interfering with one another (e.g. attempting to // delete the same read value, or reading values that another simulation is currently modifying). - return this.#putInJobQueue(async () => { + return this.#putInJobQueue(async jobId => { try { const totalTimer = new Timer(); const syncTimer = new Timer(); @@ -1001,10 +1030,16 @@ export class PXE { const contractFunctionSimulator = this.#getSimulatorForTx(); await this.contractStore.syncPrivateState(call.to, call.selector, privateSyncCall => - this.#simulateUtility(contractFunctionSimulator, privateSyncCall), + this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId), ); - const executionResult = await this.#simulateUtility(contractFunctionSimulator, call, authwits ?? [], scopes); + const executionResult = await this.#simulateUtility( + contractFunctionSimulator, + call, + authwits ?? [], + scopes, + jobId, + ); const functionTime = functionTimer.ms(); const totalTime = totalTimer.ms(); @@ -1046,14 +1081,15 @@ export class PXE { * @returns - The packed events with block and tx metadata. */ public getPrivateEvents(eventSelector: EventSelector, filter: PrivateEventFilter): Promise { - return this.#putInJobQueue(async () => { + return this.#putInJobQueue(async jobId => { await this.blockStateSynchronizer.sync(); const contractFunctionSimulator = this.#getSimulatorForTx(); await this.contractStore.syncPrivateState( filter.contractAddress, null, - async privateSyncCall => await this.#simulateUtility(contractFunctionSimulator, privateSyncCall), + async privateSyncCall => + await this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId), ); const sanitizedFilter = await new PrivateEventFilterValidator(this.anchorBlockStore).validate(filter); diff --git a/yarn-project/txe/src/constants.ts b/yarn-project/txe/src/constants.ts index 24230b9217a4..3819343918e6 100644 --- a/yarn-project/txe/src/constants.ts +++ b/yarn-project/txe/src/constants.ts @@ -1,3 +1,4 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; export const DEFAULT_ADDRESS = AztecAddress.fromNumber(42); +export const TXE_JOB_ID = 'test-job-id'; 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 3d2cf25889b7..9793e262ecdd 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 @@ -80,7 +80,7 @@ import { import type { UInt64 } from '@aztec/stdlib/types'; import { ForkCheckpoint } from '@aztec/world-state'; -import { DEFAULT_ADDRESS } from '../constants.js'; +import { DEFAULT_ADDRESS, TXE_JOB_ID } from '../constants.js'; import type { TXEStateMachine } from '../state_machine/index.js'; import type { TXEAccountStore } from '../util/txe_account_store.js'; import type { TXEContractStore } from '../util/txe_contract_store.js'; @@ -344,6 +344,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + TXE_JOB_ID, 0, 1, undefined, // log @@ -681,6 +682,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + TXE_JOB_ID, ); const acirExecutionResult = await new WASMSimulator() .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback()) diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 2a4cae464ae5..1e7e8ee4e0a9 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -43,7 +43,7 @@ import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx'; import { z } from 'zod'; -import { DEFAULT_ADDRESS } from './constants.js'; +import { DEFAULT_ADDRESS, TXE_JOB_ID } from './constants.js'; import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js'; import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js'; import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js'; @@ -337,6 +337,7 @@ export class TXESession implements TXESessionStateHandler { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + TXE_JOB_ID, ); // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary @@ -405,6 +406,7 @@ export class TXESession implements TXESessionStateHandler { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + TXE_JOB_ID, ); this.state = { name: 'UTILITY' }; @@ -499,6 +501,7 @@ export class TXESession implements TXESessionStateHandler { this.senderAddressBookStore, this.capsuleStore, this.privateEventStore, + TXE_JOB_ID, ); await new WASMSimulator() .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())