From aa949ef6f838ce4273cddd112358887bd6c26f9c Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:08:42 -0500 Subject: [PATCH] refactor: remove EnqueuedCallSimulator (#10015) The EnqueuedCallSimulator crafted public inputs for the public kernel merge circuit at the enqueued call level. Now that public inputs are being crafted per public TX and the public kernel merge isn't used, this module can be removed. --- .../src/public/enqueued_call_simulator.ts | 268 -------------- yarn-project/simulator/src/public/index.ts | 1 - .../src/public/public_processor.test.ts | 4 +- .../simulator/src/public/public_processor.ts | 10 +- .../simulator/src/public/public_tx_context.ts | 57 ++- .../src/public/public_tx_simulator.test.ts | 19 +- .../src/public/public_tx_simulator.ts | 83 +++-- .../src/public/transitional_adapters.ts | 347 ++++++++++++++++++ yarn-project/simulator/src/public/utils.ts | 168 +-------- 9 files changed, 451 insertions(+), 506 deletions(-) delete mode 100644 yarn-project/simulator/src/public/enqueued_call_simulator.ts create mode 100644 yarn-project/simulator/src/public/transitional_adapters.ts diff --git a/yarn-project/simulator/src/public/enqueued_call_simulator.ts b/yarn-project/simulator/src/public/enqueued_call_simulator.ts deleted file mode 100644 index 62b48402d3f..00000000000 --- a/yarn-project/simulator/src/public/enqueued_call_simulator.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { - type AvmProvingRequest, - MerkleTreeId, - NestedProcessReturnValues, - ProvingRequestType, - type PublicExecutionRequest, - type SimulationError, - UnencryptedFunctionL2Logs, -} from '@aztec/circuit-types'; -import { - AvmCircuitInputs, - AvmCircuitPublicInputs, - AztecAddress, - type CombinedConstantData, - ContractStorageRead, - ContractStorageUpdateRequest, - Fr, - Gas, - type GlobalVariables, - type Header, - L2ToL1Message, - LogHash, - MAX_ENQUEUED_CALLS_PER_CALL, - MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, - MAX_L2_GAS_PER_ENQUEUED_CALL, - MAX_L2_TO_L1_MSGS_PER_CALL, - MAX_NOTE_HASHES_PER_CALL, - MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - MAX_NULLIFIERS_PER_CALL, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - MAX_UNENCRYPTED_LOGS_PER_CALL, - NoteHash, - Nullifier, - type PublicCallRequest, - PublicCircuitPublicInputs, - PublicInnerCallRequest, - ReadRequest, - RevertCode, - TreeLeafReadRequest, - type VMCircuitPublicInputs, -} from '@aztec/circuits.js'; -import { computeVarArgsHash } from '@aztec/circuits.js/hash'; -import { padArrayEnd } from '@aztec/foundation/collection'; -import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { type MerkleTreeReadOperations } from '@aztec/world-state'; - -import { AvmContractCallResult } from '../avm/avm_contract_call_result.js'; -import { type AvmPersistableStateManager } from '../avm/journal/journal.js'; -import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; -import { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js'; -import { type PublicExecutor, createAvmExecutionEnvironment } from './executor.js'; -import { type WorldStateDB } from './public_db_sources.js'; - -function emptyAvmProvingRequest(): AvmProvingRequest { - return { - type: ProvingRequestType.PUBLIC_VM, - inputs: AvmCircuitInputs.empty(), - }; -} -function makeAvmProvingRequest(inputs: PublicCircuitPublicInputs, result: PublicFunctionCallResult): AvmProvingRequest { - return { - type: ProvingRequestType.PUBLIC_VM, - inputs: new AvmCircuitInputs( - result.functionName, - result.calldata, - inputs, - result.avmCircuitHints, - AvmCircuitPublicInputs.empty(), - ), - }; -} - -export type EnqueuedCallResult = { - /** Inputs to be used for proving */ - avmProvingRequest: AvmProvingRequest; - /** The public kernel output at the end of the enqueued call */ - kernelOutput: VMCircuitPublicInputs; - /** Unencrypted logs generated during the execution of this enqueued call */ - newUnencryptedLogs: UnencryptedFunctionL2Logs; - /** Return values of simulating complete callstack */ - returnValues: NestedProcessReturnValues; - /** Gas used during the execution of this enqueued call */ - gasUsed: Gas; - /** Did call revert? */ - reverted: boolean; - /** Revert reason, if any */ - revertReason?: SimulationError; -}; - -export class EnqueuedCallSimulator { - private log: DebugLogger; - constructor( - private db: MerkleTreeReadOperations, - private worldStateDB: WorldStateDB, - private publicExecutor: PublicExecutor, - private globalVariables: GlobalVariables, - private historicalHeader: Header, - private realAvmProvingRequests: boolean, - ) { - this.log = createDebugLogger(`aztec:sequencer`); - } - - async simulate( - callRequest: PublicCallRequest, - executionRequest: PublicExecutionRequest, - constants: CombinedConstantData, - availableGas: Gas, - transactionFee: Fr, - stateManager: AvmPersistableStateManager, - ): Promise { - // Gas allocated to an enqueued call can be different from the available gas - // if there is more gas available than the max allocation per enqueued call. - const allocatedGas = new Gas( - /*daGas=*/ availableGas.daGas, - /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL), - ); - - const result = (await this.publicExecutor.simulate( - stateManager, - executionRequest, - this.globalVariables, - allocatedGas, - transactionFee, - )) as EnqueuedPublicCallExecutionResult; - - /////////////////////////////////////////////////////////////////////////// - // ALL TEMPORARY - const fnName = await getPublicFunctionDebugName( - this.worldStateDB, - executionRequest.callContext.contractAddress, - executionRequest.callContext.functionSelector, - executionRequest.args, - ); - - const avmExecutionEnv = createAvmExecutionEnvironment(executionRequest, this.globalVariables, transactionFee); - const avmCallResult = new AvmContractCallResult(result.reverted, result.returnValues); - - // Generate an AVM proving request - let avmProvingRequest: AvmProvingRequest; - if (this.realAvmProvingRequests) { - const deprecatedFunctionCallResult = stateManager.trace.toPublicFunctionCallResult( - avmExecutionEnv, - /*startGasLeft=*/ allocatedGas, - /*endGasLeft=*/ Gas.from(result.endGasLeft), - Buffer.alloc(0), - avmCallResult, - fnName, - ); - const publicInputs = await this.getPublicCircuitPublicInputs(deprecatedFunctionCallResult); - avmProvingRequest = makeAvmProvingRequest(publicInputs, deprecatedFunctionCallResult); - } else { - avmProvingRequest = emptyAvmProvingRequest(); - } - - // TODO(dbanks12): Since AVM circuit will be at the level of all enqueued calls in a TX, - // this public inputs generation will move up to the enqueued calls processor. - const vmCircuitPublicInputs = stateManager.trace.toVMCircuitPublicInputs( - constants, - callRequest, - /*startGasLeft=*/ allocatedGas, - /*endGasLeft=*/ Gas.from(result.endGasLeft), - transactionFee, - avmCallResult, - ); - - const gasUsed = allocatedGas.sub(Gas.from(result.endGasLeft)); - - return { - avmProvingRequest, - kernelOutput: vmCircuitPublicInputs, - newUnencryptedLogs: new UnencryptedFunctionL2Logs(stateManager!.trace.getUnencryptedLogs()), - returnValues: new NestedProcessReturnValues(result.returnValues), - gasUsed, - reverted: result.reverted, - revertReason: result.revertReason, - }; - } - - private async getPublicCircuitPublicInputs(result: PublicFunctionCallResult) { - const publicDataTreeInfo = await this.db.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE); - this.historicalHeader.state.partial.publicDataTree.root = Fr.fromBuffer(publicDataTreeInfo.root); - - return PublicCircuitPublicInputs.from({ - callContext: result.executionRequest.callContext, - proverAddress: AztecAddress.ZERO, - argsHash: computeVarArgsHash(result.executionRequest.args), - noteHashes: padArrayEnd( - result.noteHashes, - NoteHash.empty(), - MAX_NOTE_HASHES_PER_CALL, - `Too many note hashes. Got ${result.noteHashes.length} with max being ${MAX_NOTE_HASHES_PER_CALL}`, - ), - nullifiers: padArrayEnd( - result.nullifiers, - Nullifier.empty(), - MAX_NULLIFIERS_PER_CALL, - `Too many nullifiers. Got ${result.nullifiers.length} with max being ${MAX_NULLIFIERS_PER_CALL}`, - ), - l2ToL1Msgs: padArrayEnd( - result.l2ToL1Messages, - L2ToL1Message.empty(), - MAX_L2_TO_L1_MSGS_PER_CALL, - `Too many L2 to L1 messages. Got ${result.l2ToL1Messages.length} with max being ${MAX_L2_TO_L1_MSGS_PER_CALL}`, - ), - startSideEffectCounter: result.startSideEffectCounter, - endSideEffectCounter: result.endSideEffectCounter, - returnsHash: computeVarArgsHash(result.returnValues), - noteHashReadRequests: padArrayEnd( - result.noteHashReadRequests, - TreeLeafReadRequest.empty(), - MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - `Too many note hash read requests. Got ${result.noteHashReadRequests.length} with max being ${MAX_NOTE_HASH_READ_REQUESTS_PER_CALL}`, - ), - nullifierReadRequests: padArrayEnd( - result.nullifierReadRequests, - ReadRequest.empty(), - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - `Too many nullifier read requests. Got ${result.nullifierReadRequests.length} with max being ${MAX_NULLIFIER_READ_REQUESTS_PER_CALL}`, - ), - nullifierNonExistentReadRequests: padArrayEnd( - result.nullifierNonExistentReadRequests, - ReadRequest.empty(), - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, - `Too many nullifier non-existent read requests. Got ${result.nullifierNonExistentReadRequests.length} with max being ${MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL}`, - ), - l1ToL2MsgReadRequests: padArrayEnd( - result.l1ToL2MsgReadRequests, - TreeLeafReadRequest.empty(), - MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, - `Too many L1 to L2 message read requests. Got ${result.l1ToL2MsgReadRequests.length} with max being ${MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL}`, - ), - contractStorageReads: padArrayEnd( - result.contractStorageReads, - ContractStorageRead.empty(), - MAX_PUBLIC_DATA_READS_PER_CALL, - `Too many public data reads. Got ${result.contractStorageReads.length} with max being ${MAX_PUBLIC_DATA_READS_PER_CALL}`, - ), - contractStorageUpdateRequests: padArrayEnd( - result.contractStorageUpdateRequests, - ContractStorageUpdateRequest.empty(), - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - `Too many public data update requests. Got ${result.contractStorageUpdateRequests.length} with max being ${MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL}`, - ), - publicCallRequests: padArrayEnd( - result.publicCallRequests, - PublicInnerCallRequest.empty(), - MAX_ENQUEUED_CALLS_PER_CALL, - `Too many public call requests. Got ${result.publicCallRequests.length} with max being ${MAX_ENQUEUED_CALLS_PER_CALL}`, - ), - unencryptedLogsHashes: padArrayEnd( - result.unencryptedLogsHashes, - LogHash.empty(), - MAX_UNENCRYPTED_LOGS_PER_CALL, - `Too many unencrypted logs. Got ${result.unencryptedLogsHashes.length} with max being ${MAX_UNENCRYPTED_LOGS_PER_CALL}`, - ), - historicalHeader: this.historicalHeader, - globalVariables: this.globalVariables, - startGasLeft: Gas.from(result.startGasLeft), - endGasLeft: Gas.from(result.endGasLeft), - transactionFee: result.transactionFee, - // TODO(@just-mitch): need better mapping from simulator to revert code. - revertCode: result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK, - }); - } -} diff --git a/yarn-project/simulator/src/public/index.ts b/yarn-project/simulator/src/public/index.ts index 1735b437ef1..52d955828e0 100644 --- a/yarn-project/simulator/src/public/index.ts +++ b/yarn-project/simulator/src/public/index.ts @@ -1,5 +1,4 @@ export * from './db_interfaces.js'; -export { EnqueuedCallSimulator } from './enqueued_call_simulator.js'; export * from './public_tx_simulator.js'; export { type EnqueuedPublicCallExecutionResult as PublicExecutionResult, diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index 621a833af3b..7db6b283dc8 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -79,7 +79,7 @@ describe('public_processor', () => { worldStateDB.storageRead.mockResolvedValue(Fr.ZERO); - publicTxProcessor.process.mockImplementation(() => { + publicTxProcessor.simulate.mockImplementation(() => { return Promise.resolve(mockedEnqueuedCallsResult); }); @@ -136,7 +136,7 @@ describe('public_processor', () => { }); it('returns failed txs without aborting entire operation', async function () { - publicTxProcessor.process.mockRejectedValue(new SimulationError(`Failed`, [])); + publicTxProcessor.simulate.mockRejectedValue(new SimulationError(`Failed`, [])); const tx = mockTxWithPublicCalls(); const [processed, failed] = await processor.process([tx], 1, handler); diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index f1d065bb166..421b9edd14e 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -97,13 +97,7 @@ export class PublicProcessor { worldStateDB: WorldStateDB, telemetryClient: TelemetryClient, ) { - const publicTxSimulator = PublicTxSimulator.create( - db, - publicExecutor, - globalVariables, - historicalHeader, - worldStateDB, - ); + const publicTxSimulator = PublicTxSimulator.create(db, publicExecutor, globalVariables, worldStateDB); return new PublicProcessor(db, globalVariables, historicalHeader, worldStateDB, publicTxSimulator, telemetryClient); } @@ -281,7 +275,7 @@ export class PublicProcessor { const timer = new Timer(); const { avmProvingRequest, gasUsed, revertCode, revertReason, processedPhases } = - await this.publicTxSimulator.process(tx); + await this.publicTxSimulator.simulate(tx); if (!avmProvingRequest) { this.metrics.recordFailedTx(); diff --git a/yarn-project/simulator/src/public/public_tx_context.ts b/yarn-project/simulator/src/public/public_tx_context.ts index 29810f7fe7c..c5e6faae25e 100644 --- a/yarn-project/simulator/src/public/public_tx_context.ts +++ b/yarn-project/simulator/src/public/public_tx_context.ts @@ -9,11 +9,11 @@ import { } from '@aztec/circuit-types'; import { type AvmCircuitPublicInputs, - CombinedConstantData, Fr, Gas, type GasSettings, type GlobalVariables, + type Header, type PrivateToPublicAccumulatedData, PublicAccumulatedDataArrayLengths, type PublicCallRequest, @@ -29,15 +29,15 @@ import { inspect } from 'util'; import { AvmPersistableStateManager } from '../avm/index.js'; import { DualSideEffectTrace } from './dual_side_effect_trace.js'; import { PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; +import { type EnqueuedPublicCallExecutionResult } from './execution.js'; import { type WorldStateDB } from './public_db_sources.js'; import { PublicSideEffectTrace } from './side_effect_trace.js'; -import { - convertPrivateToPublicAccumulatedData, - generateAvmCircuitPublicInputs, - getCallRequestsByPhase, - getExecutionRequestsByPhase, -} from './utils.js'; +import { generateAvmCircuitPublicInputs, generateAvmProvingRequest } from './transitional_adapters.js'; +import { convertPrivateToPublicAccumulatedData, getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js'; +/** + * The transaction-level context for public execution. + */ export class PublicTxContext { private log: DebugLogger; @@ -53,12 +53,12 @@ export class PublicTxContext { /* What caused a revert (if one occurred)? */ public revertReason: SimulationError | undefined; - public avmProvingRequest: AvmProvingRequest | undefined; // tmp hack + public avmProvingRequest: AvmProvingRequest | undefined; // FIXME(dbanks12): remove constructor( public readonly state: PhaseStateManager, private readonly globalVariables: GlobalVariables, - public readonly constants: CombinedConstantData, // FIXME(dbanks12): remove + private readonly historicalHeader: Header, // FIXME(dbanks12): remove private readonly startStateReference: StateReference, private readonly startGasUsed: Gas, private readonly gasSettings: GasSettings, @@ -114,7 +114,7 @@ export class PublicTxContext { return new PublicTxContext( new PhaseStateManager(txStateManager), globalVariables, - CombinedConstantData.combine(tx.data.constants, globalVariables), + tx.data.constants.historicalHeader, await db.getStateReference(), tx.data.gasUsed, tx.data.constants.txContext.gasSettings, @@ -334,8 +334,45 @@ export class PublicTxContext { this.avmProvingRequest!.inputs.output = this.generateAvmCircuitPublicInputs(endStateReference); return this.avmProvingRequest!; } + + // TODO(dbanks12): remove once AVM proves entire public tx + async updateProvingRequest( + real: boolean, + phase: TxExecutionPhase, + worldStateDB: WorldStateDB, + stateManager: AvmPersistableStateManager, + executionRequest: PublicExecutionRequest, + result: EnqueuedPublicCallExecutionResult, + allocatedGas: Gas, + ) { + if (this.avmProvingRequest === undefined) { + // Propagate the very first avmProvingRequest of the tx for now. + // Eventually this will be the proof for the entire public portion of the transaction. + this.avmProvingRequest = await generateAvmProvingRequest( + real, + worldStateDB, + stateManager, + this.historicalHeader, + this.globalVariables, + executionRequest, + result, + allocatedGas, + this.getTransactionFee(phase), + ); + } + } } +/** + * Thin wrapper around the state manager to handle forking and merging for phases. + * + * This lets us keep track of whether the state has already been forked + * so that we can conditionally fork at the start of a phase. + * + * There is a state manager that lives at the level of the entire transaction, + * but for setup and teardown the active state manager will be a fork of the + * transaction level one. + */ class PhaseStateManager { private currentlyActiveStateManager: AvmPersistableStateManager | undefined; diff --git a/yarn-project/simulator/src/public/public_tx_simulator.test.ts b/yarn-project/simulator/src/public/public_tx_simulator.test.ts index e2251e69ea4..cf61b574431 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.test.ts @@ -163,7 +163,6 @@ describe('public_tx_simulator', () => { db, publicExecutor, GlobalVariables.from({ ...GlobalVariables.empty(), gasFees }), - Header.empty(), worldStateDB, /*realAvmProvingRequest=*/ false, ); @@ -174,7 +173,7 @@ describe('public_tx_simulator', () => { numberOfSetupCalls: 2, }); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.SETUP, revertReason: undefined }), @@ -209,7 +208,7 @@ describe('public_tx_simulator', () => { numberOfAppLogicCalls: 2, }); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.APP_LOGIC, revertReason: undefined }), @@ -244,7 +243,7 @@ describe('public_tx_simulator', () => { hasPublicTeardownCall: true, }); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.TEARDOWN, revertReason: undefined }), @@ -279,7 +278,7 @@ describe('public_tx_simulator', () => { hasPublicTeardownCall: true, }); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ @@ -357,7 +356,7 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(publicExecutor.simulate).toHaveBeenCalledTimes(3); @@ -391,7 +390,7 @@ describe('public_tx_simulator', () => { PublicExecutionResultBuilder.empty().withReverted(setupFailure).build(), ); - await expect(processor.process(tx)).rejects.toThrow(setupFailureMsg); + await expect(processor.simulate(tx)).rejects.toThrow(setupFailureMsg); expect(publicExecutor.simulate).toHaveBeenCalledTimes(1); }); @@ -426,7 +425,7 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ @@ -504,7 +503,7 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toEqual([ expect.objectContaining({ phase: TxExecutionPhase.SETUP, revertReason: undefined }), @@ -586,7 +585,7 @@ describe('public_tx_simulator', () => { }, ]); - const txResult = await processor.process(tx); + const txResult = await processor.simulate(tx); expect(txResult.processedPhases).toHaveLength(3); expect(txResult.processedPhases).toEqual([ diff --git a/yarn-project/simulator/src/public/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator.ts index a67a223430a..0e1cc6fea0f 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.ts @@ -2,17 +2,17 @@ import { type AvmProvingRequest, type GasUsed, type MerkleTreeReadOperations, - type NestedProcessReturnValues, + NestedProcessReturnValues, type SimulationError, type Tx, TxExecutionPhase, UnencryptedFunctionL2Logs, } from '@aztec/circuit-types'; -import { type GlobalVariables, type Header, type RevertCode } from '@aztec/circuits.js'; +import { Gas, type GlobalVariables, MAX_L2_GAS_PER_ENQUEUED_CALL, type RevertCode } from '@aztec/circuits.js'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; -import { EnqueuedCallSimulator } from './enqueued_call_simulator.js'; +import { type EnqueuedPublicCallExecutionResult } from './execution.js'; import { type PublicExecutor } from './executor.js'; import { type WorldStateDB } from './public_db_sources.js'; import { PublicTxContext } from './public_tx_context.js'; @@ -42,7 +42,8 @@ export class PublicTxSimulator { private db: MerkleTreeReadOperations, private globalVariables: GlobalVariables, private worldStateDB: WorldStateDB, - private enqueuedCallSimulator: EnqueuedCallSimulator, + private publicExecutor: PublicExecutor, + private realAvmProvingRequests: boolean = true, ) { this.log = createDebugLogger(`aztec:public_tx_simulator`); } @@ -51,23 +52,13 @@ export class PublicTxSimulator { db: MerkleTreeReadOperations, publicExecutor: PublicExecutor, globalVariables: GlobalVariables, - historicalHeader: Header, worldStateDB: WorldStateDB, realAvmProvingRequests: boolean = true, ) { - const enqueuedCallSimulator = new EnqueuedCallSimulator( - db, - worldStateDB, - publicExecutor, - globalVariables, - historicalHeader, - realAvmProvingRequests, - ); - - return new PublicTxSimulator(db, globalVariables, worldStateDB, enqueuedCallSimulator); + return new PublicTxSimulator(db, globalVariables, worldStateDB, publicExecutor, realAvmProvingRequests); } - async process(tx: Tx): Promise { + async simulate(tx: Tx): Promise { this.log.verbose(`Processing tx ${tx.getTxHash()}`); const context = await PublicTxContext.create(this.db, this.worldStateDB, tx, this.globalVariables); @@ -83,15 +74,15 @@ export class PublicTxSimulator { const processedPhases: ProcessedPhase[] = []; if (context.hasPhase(TxExecutionPhase.SETUP)) { - const setupResult: ProcessedPhase = await this.processSetupPhase(context); + const setupResult: ProcessedPhase = await this.simulateSetupPhase(context); processedPhases.push(setupResult); } if (context.hasPhase(TxExecutionPhase.APP_LOGIC)) { - const appLogicResult: ProcessedPhase = await this.processAppLogicPhase(context); + const appLogicResult: ProcessedPhase = await this.simulateAppLogicPhase(context); processedPhases.push(appLogicResult); } if (context.hasPhase(TxExecutionPhase.TEARDOWN)) { - const teardownResult: ProcessedPhase = await this.processTeardownPhase(context); + const teardownResult: ProcessedPhase = await this.simulateTeardownPhase(context); processedPhases.push(teardownResult); } context.halt(); @@ -117,8 +108,6 @@ export class PublicTxSimulator { // FIXME(dbanks12): should not be changing immutable tx tx.unencryptedLogs.addFunctionLogs([new UnencryptedFunctionL2Logs(context.trace.getUnencryptedLogs())]); - await context.state.getActiveStateManager().commitStorageWritesToDB(); - return { avmProvingRequest, gasUsed: { totalGas: context.getActualGasUsed(), teardownGas: context.teardownGasUsed }, @@ -128,16 +117,16 @@ export class PublicTxSimulator { }; } - private async processSetupPhase(context: PublicTxContext): Promise { - return await this.processPhase(TxExecutionPhase.SETUP, context); + private async simulateSetupPhase(context: PublicTxContext): Promise { + return await this.simulatePhase(TxExecutionPhase.SETUP, context); } - private async processAppLogicPhase(context: PublicTxContext): Promise { + private async simulateAppLogicPhase(context: PublicTxContext): Promise { // Fork the state manager so that we can rollback state if app logic or teardown reverts. // Don't need to fork for setup since it's non-revertible (if setup fails, transaction is thrown out). context.state.fork(); - const result = await this.processPhase(TxExecutionPhase.APP_LOGIC, context); + const result = await this.simulatePhase(TxExecutionPhase.APP_LOGIC, context); if (result.reverted) { // Drop the currently active forked state manager and rollback to end of setup. @@ -152,14 +141,14 @@ export class PublicTxSimulator { return result; } - private async processTeardownPhase(context: PublicTxContext): Promise { + private async simulateTeardownPhase(context: PublicTxContext): Promise { if (!context.state.isForked()) { // If state isn't forked (app logic was empty or reverted), fork now // so we can rollback to the end of setup if teardown reverts. context.state.fork(); } - const result = await this.processPhase(TxExecutionPhase.TEARDOWN, context); + const result = await this.simulatePhase(TxExecutionPhase.TEARDOWN, context); if (result.reverted) { // Drop the currently active forked state manager and rollback to end of setup. @@ -172,7 +161,7 @@ export class PublicTxSimulator { return result; } - private async processPhase(phase: TxExecutionPhase, context: PublicTxContext): Promise { + private async simulatePhase(phase: TxExecutionPhase, context: PublicTxContext): Promise { const callRequests = context.getCallRequestsForPhase(phase); const executionRequests = context.getExecutionRequestsForPhase(phase); const txStateManager = context.state.getActiveStateManager(); @@ -191,24 +180,38 @@ export class PublicTxSimulator { const callRequest = callRequests[i]; const executionRequest = executionRequests[i]; - const enqueuedCallResult = await this.enqueuedCallSimulator.simulate( - callRequest, + const availableGas = context.getGasLeftForPhase(phase); + // Gas allocated to an enqueued call can be different from the available gas + // if there is more gas available than the max allocation per enqueued call. + const allocatedGas = new Gas( + /*daGas=*/ availableGas.daGas, + /*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_ENQUEUED_CALL), + ); + + const enqueuedCallResult = (await this.publicExecutor.simulate( + txStateManager, executionRequest, - context.constants, - /*availableGas=*/ context.getGasLeftForPhase(phase), - /*transactionFee=*/ context.getTransactionFee(phase), + this.globalVariables, // todo get from context + allocatedGas, + context.getTransactionFee(phase), + )) as EnqueuedPublicCallExecutionResult; + + // TODO(dbanks12): remove once AVM proves entire public tx + await context.updateProvingRequest( + this.realAvmProvingRequests, + phase, + this.worldStateDB, txStateManager, + executionRequest, + enqueuedCallResult, + allocatedGas, ); - if (context.avmProvingRequest === undefined) { - // Propagate the very first avmProvingRequest of the tx for now. - // Eventually this will be the proof for the entire public portion of the transaction. - context.avmProvingRequest = enqueuedCallResult.avmProvingRequest; - } txStateManager.traceEnqueuedCall(callRequest, executionRequest.args, enqueuedCallResult.reverted!); - context.consumeGas(phase, enqueuedCallResult.gasUsed); - returnValues.push(enqueuedCallResult.returnValues); + const gasUsed = allocatedGas.sub(Gas.from(enqueuedCallResult.endGasLeft)); + context.consumeGas(phase, gasUsed); + returnValues.push(new NestedProcessReturnValues(enqueuedCallResult.returnValues)); if (enqueuedCallResult.reverted) { reverted = true; diff --git a/yarn-project/simulator/src/public/transitional_adapters.ts b/yarn-project/simulator/src/public/transitional_adapters.ts new file mode 100644 index 00000000000..248b6cfd7e9 --- /dev/null +++ b/yarn-project/simulator/src/public/transitional_adapters.ts @@ -0,0 +1,347 @@ +import { type AvmProvingRequest, ProvingRequestType, type PublicExecutionRequest } from '@aztec/circuit-types'; +import { + AvmCircuitInputs, + AvmCircuitPublicInputs, + AztecAddress, + ContractStorageRead, + ContractStorageUpdateRequest, + Fr, + Gas, + type GasSettings, + type GlobalVariables, + type Header, + L2ToL1Message, + LogHash, + MAX_ENQUEUED_CALLS_PER_CALL, + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, + MAX_L2_TO_L1_MSGS_PER_CALL, + MAX_L2_TO_L1_MSGS_PER_TX, + MAX_NOTE_HASHES_PER_CALL, + MAX_NOTE_HASHES_PER_TX, + MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + MAX_NULLIFIERS_PER_CALL, + MAX_NULLIFIERS_PER_TX, + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_CALL, + NoteHash, + Nullifier, + PrivateToAvmAccumulatedData, + PrivateToAvmAccumulatedDataArrayLengths, + type PrivateToPublicAccumulatedData, + PublicCallRequest, + PublicCircuitPublicInputs, + PublicDataWrite, + PublicInnerCallRequest, + ReadRequest, + RevertCode, + type StateReference, + TreeLeafReadRequest, + TreeSnapshots, + countAccumulatedItems, + mergeAccumulatedData, +} from '@aztec/circuits.js'; +import { computeNoteHashNonce, computeUniqueNoteHash, computeVarArgsHash, siloNoteHash } from '@aztec/circuits.js/hash'; +import { padArrayEnd } from '@aztec/foundation/collection'; +import { assertLength } from '@aztec/foundation/serialize'; + +import { AvmContractCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmPersistableStateManager } from '../avm/journal/journal.js'; +import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; +import { type PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; +import { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js'; +import { createAvmExecutionEnvironment } from './executor.js'; +import { type WorldStateDB } from './public_db_sources.js'; + +export function generateAvmCircuitPublicInputs( + trace: PublicEnqueuedCallSideEffectTrace, + globalVariables: GlobalVariables, + startStateReference: StateReference, + startGasUsed: Gas, + gasSettings: GasSettings, + setupCallRequests: PublicCallRequest[], + appLogicCallRequests: PublicCallRequest[], + teardownCallRequests: PublicCallRequest[], + nonRevertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData, + revertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData, + endStateReference: StateReference, + endGasUsed: Gas, + transactionFee: Fr, + revertCode: RevertCode, +): AvmCircuitPublicInputs { + const startTreeSnapshots = new TreeSnapshots( + startStateReference.l1ToL2MessageTree, + startStateReference.partial.noteHashTree, + startStateReference.partial.nullifierTree, + startStateReference.partial.publicDataTree, + ); + const endTreeSnapshots = new TreeSnapshots( + endStateReference.l1ToL2MessageTree, + endStateReference.partial.noteHashTree, + endStateReference.partial.nullifierTree, + endStateReference.partial.publicDataTree, + ); + + const avmCircuitPublicInputs = trace.toAvmCircuitPublicInputs( + globalVariables, + startTreeSnapshots, + startGasUsed, + gasSettings, + setupCallRequests, + appLogicCallRequests, + teardownCallRequests.length ? teardownCallRequests[0] : PublicCallRequest.empty(), + endTreeSnapshots, + endGasUsed, + transactionFee, + !revertCode.isOK(), + ); + + const getArrayLengths = (from: PrivateToPublicAccumulatedData) => + new PrivateToAvmAccumulatedDataArrayLengths( + countAccumulatedItems(from.noteHashes), + countAccumulatedItems(from.nullifiers), + countAccumulatedItems(from.l2ToL1Msgs), + ); + const convertAccumulatedData = (from: PrivateToPublicAccumulatedData) => + new PrivateToAvmAccumulatedData(from.noteHashes, from.nullifiers, from.l2ToL1Msgs); + // Temporary overrides as these entries aren't yet populated in trace + avmCircuitPublicInputs.previousNonRevertibleAccumulatedDataArrayLengths = getArrayLengths( + nonRevertibleAccumulatedDataFromPrivate, + ); + avmCircuitPublicInputs.previousRevertibleAccumulatedDataArrayLengths = getArrayLengths( + revertibleAccumulatedDataFromPrivate, + ); + avmCircuitPublicInputs.previousNonRevertibleAccumulatedData = convertAccumulatedData( + nonRevertibleAccumulatedDataFromPrivate, + ); + avmCircuitPublicInputs.previousRevertibleAccumulatedData = convertAccumulatedData( + revertibleAccumulatedDataFromPrivate, + ); + + // merge all revertible & non-revertible side effects into output accumulated data + const noteHashesFromPrivate = revertCode.isOK() + ? mergeAccumulatedData( + avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes, + avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes, + ) + : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes; + avmCircuitPublicInputs.accumulatedData.noteHashes = assertLength( + mergeAccumulatedData(noteHashesFromPrivate, avmCircuitPublicInputs.accumulatedData.noteHashes), + MAX_NOTE_HASHES_PER_TX, + ); + + const txHash = avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers[0]; + + const scopedNoteHashesFromPublic = trace.getSideEffects().noteHashes; + for (let i = 0; i < scopedNoteHashesFromPublic.length; i++) { + const scopedNoteHash = scopedNoteHashesFromPublic[i]; + const noteHash = scopedNoteHash.value; + if (!noteHash.isZero()) { + const noteHashIndexInTx = i + countAccumulatedItems(noteHashesFromPrivate); + const nonce = computeNoteHashNonce(txHash, noteHashIndexInTx); + const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash); + const siloedNoteHash = siloNoteHash(scopedNoteHash.contractAddress, uniqueNoteHash); + avmCircuitPublicInputs.accumulatedData.noteHashes[noteHashIndexInTx] = siloedNoteHash; + } + } + + const nullifiersFromPrivate = revertCode.isOK() + ? mergeAccumulatedData( + avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers, + avmCircuitPublicInputs.previousRevertibleAccumulatedData.nullifiers, + ) + : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers; + avmCircuitPublicInputs.accumulatedData.nullifiers = assertLength( + mergeAccumulatedData(nullifiersFromPrivate, avmCircuitPublicInputs.accumulatedData.nullifiers), + MAX_NULLIFIERS_PER_TX, + ); + const msgsFromPrivate = revertCode.isOK() + ? mergeAccumulatedData( + avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs, + avmCircuitPublicInputs.previousRevertibleAccumulatedData.l2ToL1Msgs, + ) + : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs; + avmCircuitPublicInputs.accumulatedData.l2ToL1Msgs = assertLength( + mergeAccumulatedData(msgsFromPrivate, avmCircuitPublicInputs.accumulatedData.l2ToL1Msgs), + MAX_L2_TO_L1_MSGS_PER_TX, + ); + + const dedupedPublicDataWrites: Array = []; + const leafSlotOccurences: Map = new Map(); + for (const publicDataWrite of avmCircuitPublicInputs.accumulatedData.publicDataWrites) { + const slot = publicDataWrite.leafSlot.toBigInt(); + const prevOccurrences = leafSlotOccurences.get(slot) || 0; + leafSlotOccurences.set(slot, prevOccurrences + 1); + } + + for (const publicDataWrite of avmCircuitPublicInputs.accumulatedData.publicDataWrites) { + const slot = publicDataWrite.leafSlot.toBigInt(); + const prevOccurrences = leafSlotOccurences.get(slot) || 0; + if (prevOccurrences === 1) { + dedupedPublicDataWrites.push(publicDataWrite); + } else { + leafSlotOccurences.set(slot, prevOccurrences - 1); + } + } + + avmCircuitPublicInputs.accumulatedData.publicDataWrites = padArrayEnd( + dedupedPublicDataWrites, + PublicDataWrite.empty(), + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ); + //console.log(`AvmCircuitPublicInputs:\n${inspect(avmCircuitPublicInputs)}`); + return avmCircuitPublicInputs; +} + +export async function generateAvmProvingRequest( + real: boolean, + worldStateDB: WorldStateDB, + stateManager: AvmPersistableStateManager, + historicalHeader: Header, + globalVariables: GlobalVariables, + executionRequest: PublicExecutionRequest, + result: EnqueuedPublicCallExecutionResult, + allocatedGas: Gas, + transactionFee: Fr, +): Promise { + const fnName = await getPublicFunctionDebugName( + worldStateDB, + executionRequest.callContext.contractAddress, + executionRequest.callContext.functionSelector, + executionRequest.args, + ); + + const avmExecutionEnv = createAvmExecutionEnvironment(executionRequest, globalVariables, transactionFee); + const avmCallResult = new AvmContractCallResult(result.reverted, result.returnValues); + + // Generate an AVM proving request + let avmProvingRequest: AvmProvingRequest; + if (real) { + const deprecatedFunctionCallResult = stateManager.trace.toPublicFunctionCallResult( + avmExecutionEnv, + /*startGasLeft=*/ allocatedGas, + /*endGasLeft=*/ Gas.from(result.endGasLeft), + Buffer.alloc(0), + avmCallResult, + fnName, + ); + const publicInputs = getPublicCircuitPublicInputs(historicalHeader, globalVariables, deprecatedFunctionCallResult); + avmProvingRequest = makeAvmProvingRequest(publicInputs, deprecatedFunctionCallResult); + } else { + avmProvingRequest = emptyAvmProvingRequest(); + } + return avmProvingRequest; +} + +function emptyAvmProvingRequest(): AvmProvingRequest { + return { + type: ProvingRequestType.PUBLIC_VM, + inputs: AvmCircuitInputs.empty(), + }; +} +function makeAvmProvingRequest(inputs: PublicCircuitPublicInputs, result: PublicFunctionCallResult): AvmProvingRequest { + return { + type: ProvingRequestType.PUBLIC_VM, + inputs: new AvmCircuitInputs( + result.functionName, + result.calldata, + inputs, + result.avmCircuitHints, + AvmCircuitPublicInputs.empty(), + ), + }; +} + +function getPublicCircuitPublicInputs( + historicalHeader: Header, + globalVariables: GlobalVariables, + result: PublicFunctionCallResult, +) { + const header = historicalHeader.clone(); // don't modify the original + header.state.partial.publicDataTree.root = Fr.zero(); // AVM doesn't check this yet + + return PublicCircuitPublicInputs.from({ + callContext: result.executionRequest.callContext, + proverAddress: AztecAddress.ZERO, + argsHash: computeVarArgsHash(result.executionRequest.args), + noteHashes: padArrayEnd( + result.noteHashes, + NoteHash.empty(), + MAX_NOTE_HASHES_PER_CALL, + `Too many note hashes. Got ${result.noteHashes.length} with max being ${MAX_NOTE_HASHES_PER_CALL}`, + ), + nullifiers: padArrayEnd( + result.nullifiers, + Nullifier.empty(), + MAX_NULLIFIERS_PER_CALL, + `Too many nullifiers. Got ${result.nullifiers.length} with max being ${MAX_NULLIFIERS_PER_CALL}`, + ), + l2ToL1Msgs: padArrayEnd( + result.l2ToL1Messages, + L2ToL1Message.empty(), + MAX_L2_TO_L1_MSGS_PER_CALL, + `Too many L2 to L1 messages. Got ${result.l2ToL1Messages.length} with max being ${MAX_L2_TO_L1_MSGS_PER_CALL}`, + ), + startSideEffectCounter: result.startSideEffectCounter, + endSideEffectCounter: result.endSideEffectCounter, + returnsHash: computeVarArgsHash(result.returnValues), + noteHashReadRequests: padArrayEnd( + result.noteHashReadRequests, + TreeLeafReadRequest.empty(), + MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + `Too many note hash read requests. Got ${result.noteHashReadRequests.length} with max being ${MAX_NOTE_HASH_READ_REQUESTS_PER_CALL}`, + ), + nullifierReadRequests: padArrayEnd( + result.nullifierReadRequests, + ReadRequest.empty(), + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, + `Too many nullifier read requests. Got ${result.nullifierReadRequests.length} with max being ${MAX_NULLIFIER_READ_REQUESTS_PER_CALL}`, + ), + nullifierNonExistentReadRequests: padArrayEnd( + result.nullifierNonExistentReadRequests, + ReadRequest.empty(), + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, + `Too many nullifier non-existent read requests. Got ${result.nullifierNonExistentReadRequests.length} with max being ${MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL}`, + ), + l1ToL2MsgReadRequests: padArrayEnd( + result.l1ToL2MsgReadRequests, + TreeLeafReadRequest.empty(), + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, + `Too many L1 to L2 message read requests. Got ${result.l1ToL2MsgReadRequests.length} with max being ${MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL}`, + ), + contractStorageReads: padArrayEnd( + result.contractStorageReads, + ContractStorageRead.empty(), + MAX_PUBLIC_DATA_READS_PER_CALL, + `Too many public data reads. Got ${result.contractStorageReads.length} with max being ${MAX_PUBLIC_DATA_READS_PER_CALL}`, + ), + contractStorageUpdateRequests: padArrayEnd( + result.contractStorageUpdateRequests, + ContractStorageUpdateRequest.empty(), + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + `Too many public data update requests. Got ${result.contractStorageUpdateRequests.length} with max being ${MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL}`, + ), + publicCallRequests: padArrayEnd( + result.publicCallRequests, + PublicInnerCallRequest.empty(), + MAX_ENQUEUED_CALLS_PER_CALL, + `Too many public call requests. Got ${result.publicCallRequests.length} with max being ${MAX_ENQUEUED_CALLS_PER_CALL}`, + ), + unencryptedLogsHashes: padArrayEnd( + result.unencryptedLogsHashes, + LogHash.empty(), + MAX_UNENCRYPTED_LOGS_PER_CALL, + `Too many unencrypted logs. Got ${result.unencryptedLogsHashes.length} with max being ${MAX_UNENCRYPTED_LOGS_PER_CALL}`, + ), + historicalHeader: header, + globalVariables: globalVariables, + startGasLeft: Gas.from(result.startGasLeft), + endGasLeft: Gas.from(result.endGasLeft), + transactionFee: result.transactionFee, + // TODO(@just-mitch): need better mapping from simulator to revert code. + revertCode: result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK, + }); +} diff --git a/yarn-project/simulator/src/public/utils.ts b/yarn-project/simulator/src/public/utils.ts index 5b1713d70aa..09379f2daf3 100644 --- a/yarn-project/simulator/src/public/utils.ts +++ b/yarn-project/simulator/src/public/utils.ts @@ -1,31 +1,5 @@ import { type PublicExecutionRequest, type Tx, TxExecutionPhase } from '@aztec/circuit-types'; -import { - type AvmCircuitPublicInputs, - type Fr, - type Gas, - type GasSettings, - type GlobalVariables, - MAX_L2_TO_L1_MSGS_PER_TX, - MAX_NOTE_HASHES_PER_TX, - MAX_NULLIFIERS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - PrivateToAvmAccumulatedData, - PrivateToAvmAccumulatedDataArrayLengths, - type PrivateToPublicAccumulatedData, - PublicAccumulatedData, - PublicCallRequest, - PublicDataWrite, - type RevertCode, - type StateReference, - TreeSnapshots, - countAccumulatedItems, - mergeAccumulatedData, -} from '@aztec/circuits.js'; -import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; -import { padArrayEnd } from '@aztec/foundation/collection'; -import { assertLength } from '@aztec/foundation/serialize'; - -import { type PublicEnqueuedCallSideEffectTrace } from './enqueued_call_side_effect_trace.js'; +import { type PrivateToPublicAccumulatedData, PublicAccumulatedData, type PublicCallRequest } from '@aztec/circuits.js'; export function getExecutionRequestsByPhase(tx: Tx, phase: TxExecutionPhase): PublicExecutionRequest[] { switch (phase) { @@ -71,143 +45,3 @@ export function convertPrivateToPublicAccumulatedData( to.publicCallStack.forEach((_, i) => (to.publicCallStack[i] = fromPrivate.publicCallRequests[i])); return to; } - -export function generateAvmCircuitPublicInputs( - trace: PublicEnqueuedCallSideEffectTrace, - globalVariables: GlobalVariables, - startStateReference: StateReference, - startGasUsed: Gas, - gasSettings: GasSettings, - setupCallRequests: PublicCallRequest[], - appLogicCallRequests: PublicCallRequest[], - teardownCallRequests: PublicCallRequest[], - nonRevertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData, - revertibleAccumulatedDataFromPrivate: PrivateToPublicAccumulatedData, - endStateReference: StateReference, - endGasUsed: Gas, - transactionFee: Fr, - revertCode: RevertCode, -): AvmCircuitPublicInputs { - const startTreeSnapshots = new TreeSnapshots( - startStateReference.l1ToL2MessageTree, - startStateReference.partial.noteHashTree, - startStateReference.partial.nullifierTree, - startStateReference.partial.publicDataTree, - ); - const endTreeSnapshots = new TreeSnapshots( - endStateReference.l1ToL2MessageTree, - endStateReference.partial.noteHashTree, - endStateReference.partial.nullifierTree, - endStateReference.partial.publicDataTree, - ); - - const avmCircuitPublicInputs = trace.toAvmCircuitPublicInputs( - globalVariables, - startTreeSnapshots, - startGasUsed, - gasSettings, - setupCallRequests, - appLogicCallRequests, - teardownCallRequests.length ? teardownCallRequests[0] : PublicCallRequest.empty(), - endTreeSnapshots, - endGasUsed, - transactionFee, - !revertCode.isOK(), - ); - - const getArrayLengths = (from: PrivateToPublicAccumulatedData) => - new PrivateToAvmAccumulatedDataArrayLengths( - countAccumulatedItems(from.noteHashes), - countAccumulatedItems(from.nullifiers), - countAccumulatedItems(from.l2ToL1Msgs), - ); - const convertAccumulatedData = (from: PrivateToPublicAccumulatedData) => - new PrivateToAvmAccumulatedData(from.noteHashes, from.nullifiers, from.l2ToL1Msgs); - // Temporary overrides as these entries aren't yet populated in trace - avmCircuitPublicInputs.previousNonRevertibleAccumulatedDataArrayLengths = getArrayLengths( - nonRevertibleAccumulatedDataFromPrivate, - ); - avmCircuitPublicInputs.previousRevertibleAccumulatedDataArrayLengths = getArrayLengths( - revertibleAccumulatedDataFromPrivate, - ); - avmCircuitPublicInputs.previousNonRevertibleAccumulatedData = convertAccumulatedData( - nonRevertibleAccumulatedDataFromPrivate, - ); - avmCircuitPublicInputs.previousRevertibleAccumulatedData = convertAccumulatedData( - revertibleAccumulatedDataFromPrivate, - ); - - // merge all revertible & non-revertible side effects into output accumulated data - const noteHashesFromPrivate = revertCode.isOK() - ? mergeAccumulatedData( - avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes, - avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes, - ) - : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes; - avmCircuitPublicInputs.accumulatedData.noteHashes = assertLength( - mergeAccumulatedData(noteHashesFromPrivate, avmCircuitPublicInputs.accumulatedData.noteHashes), - MAX_NOTE_HASHES_PER_TX, - ); - - const txHash = avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers[0]; - - const scopedNoteHashesFromPublic = trace.getSideEffects().noteHashes; - for (let i = 0; i < scopedNoteHashesFromPublic.length; i++) { - const scopedNoteHash = scopedNoteHashesFromPublic[i]; - const noteHash = scopedNoteHash.value; - if (!noteHash.isZero()) { - const noteHashIndexInTx = i + countAccumulatedItems(noteHashesFromPrivate); - const nonce = computeNoteHashNonce(txHash, noteHashIndexInTx); - const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash); - const siloedNoteHash = siloNoteHash(scopedNoteHash.contractAddress, uniqueNoteHash); - avmCircuitPublicInputs.accumulatedData.noteHashes[noteHashIndexInTx] = siloedNoteHash; - } - } - - const nullifiersFromPrivate = revertCode.isOK() - ? mergeAccumulatedData( - avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers, - avmCircuitPublicInputs.previousRevertibleAccumulatedData.nullifiers, - ) - : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers; - avmCircuitPublicInputs.accumulatedData.nullifiers = assertLength( - mergeAccumulatedData(nullifiersFromPrivate, avmCircuitPublicInputs.accumulatedData.nullifiers), - MAX_NULLIFIERS_PER_TX, - ); - const msgsFromPrivate = revertCode.isOK() - ? mergeAccumulatedData( - avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs, - avmCircuitPublicInputs.previousRevertibleAccumulatedData.l2ToL1Msgs, - ) - : avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs; - avmCircuitPublicInputs.accumulatedData.l2ToL1Msgs = assertLength( - mergeAccumulatedData(msgsFromPrivate, avmCircuitPublicInputs.accumulatedData.l2ToL1Msgs), - MAX_L2_TO_L1_MSGS_PER_TX, - ); - - const dedupedPublicDataWrites: Array = []; - const leafSlotOccurences: Map = new Map(); - for (const publicDataWrite of avmCircuitPublicInputs.accumulatedData.publicDataWrites) { - const slot = publicDataWrite.leafSlot.toBigInt(); - const prevOccurrences = leafSlotOccurences.get(slot) || 0; - leafSlotOccurences.set(slot, prevOccurrences + 1); - } - - for (const publicDataWrite of avmCircuitPublicInputs.accumulatedData.publicDataWrites) { - const slot = publicDataWrite.leafSlot.toBigInt(); - const prevOccurrences = leafSlotOccurences.get(slot) || 0; - if (prevOccurrences === 1) { - dedupedPublicDataWrites.push(publicDataWrite); - } else { - leafSlotOccurences.set(slot, prevOccurrences - 1); - } - } - - avmCircuitPublicInputs.accumulatedData.publicDataWrites = padArrayEnd( - dedupedPublicDataWrites, - PublicDataWrite.empty(), - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); - //console.log(`AvmCircuitPublicInputs:\n${inspect(avmCircuitPublicInputs)}`); - return avmCircuitPublicInputs; -}