diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index d938ae457667..2239c271bb20 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -503,9 +503,9 @@ export class PXEService implements PXE { return await this.node.getBlock(blockNumber); } - public proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise { + public proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean, scopes?: AztecAddress[]): Promise { return this.jobQueue.put(async () => { - const simulatedTx = await this.#simulateAndProve(txRequest, this.proofCreator, undefined); + const simulatedTx = await this.#simulateAndProve(txRequest, this.proofCreator, undefined, scopes); if (simulatePublic) { simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx); } @@ -518,9 +518,10 @@ export class PXEService implements PXE { txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender: AztecAddress | undefined = undefined, + scopes?: AztecAddress[], ): Promise { return await this.jobQueue.put(async () => { - const simulatedTx = await this.#simulateAndProve(txRequest, this.fakeProofCreator, msgSender); + const simulatedTx = await this.#simulateAndProve(txRequest, this.fakeProofCreator, msgSender, scopes); if (simulatePublic) { simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx); } @@ -551,12 +552,13 @@ export class PXEService implements PXE { args: any[], to: AztecAddress, _from?: AztecAddress, + scopes?: AztecAddress[], ): Promise { // all simulations must be serialized w.r.t. the synchronizer return await this.jobQueue.put(async () => { // TODO - Should check if `from` has the permission to call the view function. const functionCall = await this.#getFunctionCall(functionName, args, to); - const executionResult = await this.#simulateUnconstrained(functionCall); + const executionResult = await this.#simulateUnconstrained(functionCall, scopes); // TODO - Return typed result based on the function artifact. return executionResult; @@ -668,14 +670,18 @@ export class PXEService implements PXE { }; } - async #simulate(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise { + async #simulate( + txRequest: TxExecutionRequest, + msgSender?: AztecAddress, + scopes?: AztecAddress[], + ): Promise { // TODO - Pause syncing while simulating. const { contractAddress, functionArtifact } = await this.#getSimulationParameters(txRequest); this.log.debug('Executing simulator...'); try { - const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender); + const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender, scopes); this.log.verbose(`Simulation completed for ${contractAddress.toString()}:${functionArtifact.name}`); return result; } catch (err) { @@ -692,14 +698,15 @@ export class PXEService implements PXE { * Returns the simulation result containing the outputs of the unconstrained function. * * @param execRequest - The transaction request object containing the target contract and function data. + * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns The simulation result containing the outputs of the unconstrained function. */ - async #simulateUnconstrained(execRequest: FunctionCall) { + async #simulateUnconstrained(execRequest: FunctionCall, scopes?: AztecAddress[]) { const { contractAddress, functionArtifact } = await this.#getSimulationParameters(execRequest); this.log.debug('Executing unconstrained simulator...'); try { - const result = await this.simulator.runUnconstrained(execRequest, functionArtifact, contractAddress); + const result = await this.simulator.runUnconstrained(execRequest, functionArtifact, contractAddress, scopes); this.log.verbose(`Unconstrained simulation for ${contractAddress}.${functionArtifact.name} completed`); return result; @@ -757,6 +764,7 @@ export class PXEService implements PXE { * @param txExecutionRequest - The transaction request to be simulated and proved. * @param proofCreator - The proof creator to use for proving the execution. * @param msgSender - (Optional) The message sender to use for the simulation. + * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns An object that contains: * A private transaction object containing the proof, public inputs, and encrypted logs. * The return values of the private execution @@ -765,9 +773,10 @@ export class PXEService implements PXE { txExecutionRequest: TxExecutionRequest, proofCreator: PrivateKernelProver, msgSender?: AztecAddress, + scopes?: AztecAddress[], ): Promise { // Get values that allow us to reconstruct the block hash - const executionResult = await this.#simulate(txExecutionRequest, msgSender); + const executionResult = await this.#simulate(txExecutionRequest, msgSender, scopes); const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node); const kernelProver = new KernelProver(kernelOracle, proofCreator); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index ce517998b2e1..f6bc028cb30f 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -77,11 +77,12 @@ export class SimulatorOracle implements DBOracle { return capsule; } - async getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus) { + async getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus, scopes?: AztecAddress[]) { const noteDaos = await this.db.getIncomingNotes({ contractAddress, storageSlot, status, + accounts: scopes, }); return noteDaos.map(({ contractAddress, storageSlot, nonce, note, slottedNoteHash, siloedNullifier, index }) => ({ contractAddress, diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index e79d6ea6c12f..c785bc57013a 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -94,8 +94,9 @@ export class ClientExecutionContext extends ViewDataOracle { private node: AztecNode, protected sideEffectCounter: number = 0, log = createDebugLogger('aztec:simulator:client_execution_context'), + scopes?: AztecAddress[], ) { - super(contractAddress, authWitnesses, db, node, log); + super(contractAddress, authWitnesses, db, node, log, scopes); } // We still need this function until we can get user-defined ordering of structs for fn arguments @@ -251,7 +252,7 @@ export class ClientExecutionContext extends ViewDataOracle { const pendingNotes = this.noteCache.getNotes(this.callContext.storageContractAddress, storageSlot); const pendingNullifiers = this.noteCache.getNullifiers(this.callContext.storageContractAddress); - const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status); + const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status, this.scopes); const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value)); const notes = pickNotes([...dbNotesFiltered, ...pendingNotes], { diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 5aa044dc5c91..7dc81d71d0af 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -81,9 +81,15 @@ export interface DBOracle extends CommitmentsDB { * @param contractAddress - The contract address of the notes. * @param storageSlot - The storage slot of the notes. * @param status - The status of notes to fetch. + * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns A Promise that resolves to an array of note data. */ - getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus): Promise; + getNotes( + contractAddress: AztecAddress, + storageSlot: Fr, + status: NoteStatus, + scopes?: AztecAddress[], + ): Promise; /** * Retrieve the artifact information of a specific function within a contract. diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index e23bb42ea6dc..780c6b5626d7 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -38,6 +38,7 @@ export class AcirSimulator { * @param entryPointArtifact - The artifact of the entry point function. * @param contractAddress - The address of the contract (should match request.origin) * @param msgSender - The address calling the function. This can be replaced to simulate a call from another contract or a specific account. + * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns The result of the execution. */ public async run( @@ -45,6 +46,7 @@ export class AcirSimulator { entryPointArtifact: FunctionArtifact, contractAddress: AztecAddress, msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), + scopes?: AztecAddress[], ): Promise { if (entryPointArtifact.functionType !== FunctionType.PRIVATE) { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as private`); @@ -80,6 +82,8 @@ export class AcirSimulator { this.db, this.node, startSideEffectCounter, + undefined, + scopes, ); try { @@ -100,18 +104,19 @@ export class AcirSimulator { * @param request - The transaction request. * @param entryPointArtifact - The artifact of the entry point function. * @param contractAddress - The address of the contract. - * @param aztecNode - The AztecNode instance. + * @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all. */ public async runUnconstrained( request: FunctionCall, entryPointArtifact: FunctionArtifact, contractAddress: AztecAddress, + scopes?: AztecAddress[], ) { if (entryPointArtifact.functionType !== FunctionType.UNCONSTRAINED) { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as unconstrained`); } - const context = new ViewDataOracle(contractAddress, [], this.db, this.node); + const context = new ViewDataOracle(contractAddress, [], this.db, this.node, undefined, scopes); try { return await executeUnconstrainedFunction( @@ -192,6 +197,7 @@ export class AcirSimulator { execRequest, artifact, contractAddress, + // We can omit scopes here, because "compute_note_hash_and_optionally_a_nullifier" does not need access to any notes. )) as bigint[]; return { diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 75e7f1802338..6a90a3efccf9 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -30,6 +30,7 @@ export class ViewDataOracle extends TypedOracle { protected readonly db: DBOracle, protected readonly aztecNode: AztecNode, protected log = createDebugLogger('aztec:simulator:client_view_context'), + protected readonly scopes?: AztecAddress[], ) { super(); } @@ -219,7 +220,7 @@ export class ViewDataOracle extends TypedOracle { offset: number, status: NoteStatus, ): Promise { - const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); + const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status, this.scopes); return pickNotes(dbNotes, { selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({ selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] },