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 62b48402d3fb..000000000000 --- 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 1735b437ef10..52d955828e09 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 621a833af3b4..7db6b283dc8a 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 f1d065bb1665..421b9edd14e7 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 29810f7fe7c0..c5e6faae25e7 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 e2251e69ea45..cf61b5744317 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 a67a223430ab..0e1cc6fea0ff 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 000000000000..248b6cfd7e94 --- /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 5b1713d70aad..09379f2daf32 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; -}