diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index dc6d5bcb8131..abb9c049c82d 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -159,11 +159,12 @@ export interface PXE { * * @param txRequest - An authenticated tx request ready for simulation * @param simulatePublic - Whether to simulate the public part of the transaction. + * @param scopes - (Optional) The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns A transaction ready to be sent to the network for execution. * @throws If the code for the functions executed in this transaction has not been made available via `addContracts`. * Also throws if simulatePublic is true and public simulation reverts. */ - proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise; + proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean, scopes?: AztecAddress[]): Promise; /** * Simulates a transaction based on the provided preauthenticated execution request. @@ -179,12 +180,13 @@ export interface PXE { * @param txRequest - An authenticated tx request ready for simulation * @param simulatePublic - Whether to simulate the public part of the transaction. * @param msgSender - (Optional) The message sender to use for the simulation. + * @param scopes - (Optional) The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns A simulated transaction object that includes a transaction that is potentially ready * to be sent to the network for execution, along with public and private return values. * @throws If the code for the functions executed in this transaction has not been made available via `addContracts`. * Also throws if simulatePublic is true and public simulation reverts. */ - simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender?: AztecAddress): Promise; + simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender?: AztecAddress, scopes?: AztecAddress[]): Promise; /** * Sends a transaction to an Aztec node to be broadcasted to the network and mined. @@ -280,9 +282,10 @@ export interface PXE { * @param args - The arguments to be provided to the function. * @param to - The address of the contract to be called. * @param from - (Optional) The msg sender to set for the call. + * @param scopes - (Optional) The accounts whose notes we can access in this call. Currently optional and will default to all. * @returns The result of the view function call, structured based on the function ABI. */ - simulateUnconstrained(functionName: string, args: any[], to: AztecAddress, from?: AztecAddress): Promise; + simulateUnconstrained(functionName: string, args: any[], to: AztecAddress, from?: AztecAddress, scopes?: AztecAddress[]): Promise; /** * Gets unencrypted logs based on the provided filter. diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 337156873764..c6e9f378a769 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -502,9 +502,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); } @@ -517,9 +517,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); } @@ -550,12 +551,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; @@ -667,14 +669,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) { @@ -691,14 +697,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; @@ -756,6 +763,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 @@ -764,9 +772,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..deab8e878d7a 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, + 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 5b16971e641d..cab286463ca6 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], { @@ -519,6 +520,8 @@ export class ClientExecutionContext extends ViewDataOracle { this.db, this.node, sideEffectCounter, + this.log, + this.scopes, ); const childExecutionResult = await executePrivateFunction( 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 79e61faea771..260282c67a1b 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`); @@ -83,6 +85,8 @@ export class AcirSimulator { this.db, this.node, startSideEffectCounter, + undefined, + scopes, ); try { @@ -103,18 +107,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( @@ -195,6 +200,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] },