diff --git a/yarn-project/simulator/src/avm/avm_context.ts b/yarn-project/simulator/src/avm/avm_context.ts index 2fa4dde51321..ee6d1c15ee62 100644 --- a/yarn-project/simulator/src/avm/avm_context.ts +++ b/yarn-project/simulator/src/avm/avm_context.ts @@ -4,6 +4,7 @@ import { type Fr } from '@aztec/foundation/fields'; import { type AvmExecutionEnvironment } from './avm_execution_environment.js'; import { type Gas, gasToGasLeft } from './avm_gas.js'; import { AvmMachineState } from './avm_machine_state.js'; +import type { AvmSimulator } from './avm_simulator.js'; import { type AvmPersistableStateManager } from './journal/journal.js'; /** @@ -24,6 +25,10 @@ export class AvmContext { public machineState: AvmMachineState, ) {} + // This is needed to break a dependency cycle created by the CALL opcode, + // which needs to create a new simulator but cannot depend directly on AvmSimulator. + public provideSimulator?: (ctx: this) => Promise; + /** * Prepare a new AVM context that will be ready for an external/nested call * - Fork the world state journal diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index 686de00cfcb6..871829f750c5 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -44,9 +44,12 @@ export class AvmSimulator { // only. Otherwise, use build() below. constructor( private context: AvmContext, - private instructionSet: InstructionSet = INSTRUCTION_SET(), + private instructionSet: InstructionSet = INSTRUCTION_SET, enableTallying = false, ) { + // This will be used by the CALL opcode to create a new simulator. It is required to + // avoid a dependency cycle. + context.provideSimulator = AvmSimulator.build; assert( context.machineState.gasLeft.l2Gas <= MAX_L2_GAS_PER_TX_PUBLIC_PORTION, `Cannot allocate more than ${MAX_L2_GAS_PER_TX_PUBLIC_PORTION} to the AVM for execution.`, diff --git a/yarn-project/simulator/src/avm/fixtures/index.ts b/yarn-project/simulator/src/avm/fixtures/index.ts index 8ef8937470e2..bacb8f1c017c 100644 --- a/yarn-project/simulator/src/avm/fixtures/index.ts +++ b/yarn-project/simulator/src/avm/fixtures/index.ts @@ -13,7 +13,7 @@ import merge from 'lodash.merge'; import { resolveAssertionMessageFromRevertData, traverseCauseChain } from '../../common.js'; import { type PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js'; -import { type WorldStateDB } from '../../server.js'; +import { AvmSimulator, type WorldStateDB } from '../../server.js'; import { AvmContext } from '../avm_context.js'; import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; import { AvmMachineState } from '../avm_machine_state.js'; @@ -34,11 +34,13 @@ export function initContext(overrides?: { env?: AvmExecutionEnvironment; machineState?: AvmMachineState; }): AvmContext { - return new AvmContext( + const ctx = new AvmContext( overrides?.persistableState || initPersistableStateManager(), overrides?.env || initExecutionEnvironment(), overrides?.machineState || initMachineState(), ); + ctx.provideSimulator = AvmSimulator.build; + return ctx; } /** Creates an empty state manager with mocked host storage. */ diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.ts index d8884a274c2b..a7f174d09cc4 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.ts @@ -1,7 +1,6 @@ import type { AvmContext } from '../avm_context.js'; import { type AvmContractCallResult } from '../avm_contract_call_result.js'; import { type Field, TypeTag, Uint1 } from '../avm_memory_types.js'; -import { AvmSimulator } from '../avm_simulator.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Addressing } from './addressing_mode.js'; import { Instruction } from './instruction.js'; @@ -62,7 +61,7 @@ abstract class ExternalCall extends Instruction { const aztecAddress = callAddress.toAztecAddress(); const nestedContext = context.createNestedContractCallContext(aztecAddress, calldata, allocatedGas, callType); - const simulator = await AvmSimulator.build(nestedContext); + const simulator = await context.provideSimulator!(nestedContext); const nestedCallResults: AvmContractCallResult = await simulator.execute(); const success = !nestedCallResults.reverted; diff --git a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts index aaade1ead237..f0c00cf46029 100644 --- a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts @@ -62,94 +62,91 @@ export interface Deserializable { } export type InstructionSet = Map; -// TODO(4359): This is a function so that Call and StaticCall can be lazily resolved. -// This is a temporary solution until we solve the dependency cycle. -export const INSTRUCTION_SET = () => - new Map([ - [Opcode.ADD_8, Add.as(Add.wireFormat8).deserialize], - [Opcode.ADD_16, Add.as(Add.wireFormat16).deserialize], - [Opcode.SUB_8, Sub.as(Sub.wireFormat8).deserialize], - [Opcode.SUB_16, Sub.as(Sub.wireFormat16).deserialize], - [Opcode.MUL_8, Mul.as(Mul.wireFormat8).deserialize], - [Opcode.MUL_16, Mul.as(Mul.wireFormat16).deserialize], - [Opcode.DIV_8, Div.as(Div.wireFormat8).deserialize], - [Opcode.DIV_16, Div.as(Div.wireFormat16).deserialize], - [Opcode.FDIV_8, FieldDiv.as(FieldDiv.wireFormat8).deserialize], - [Opcode.FDIV_16, FieldDiv.as(FieldDiv.wireFormat16).deserialize], - [Opcode.EQ_8, Eq.as(Eq.wireFormat8).deserialize], - [Opcode.EQ_16, Eq.as(Eq.wireFormat16).deserialize], - [Opcode.LT_8, Lt.as(Lt.wireFormat8).deserialize], - [Opcode.LT_16, Lt.as(Lt.wireFormat16).deserialize], - [Opcode.LTE_8, Lte.as(Lte.wireFormat8).deserialize], - [Opcode.LTE_16, Lte.as(Lte.wireFormat16).deserialize], - [Opcode.AND_8, And.as(And.wireFormat8).deserialize], - [Opcode.AND_16, And.as(And.wireFormat16).deserialize], - [Opcode.OR_8, Or.as(Or.wireFormat8).deserialize], - [Opcode.OR_16, Or.as(Or.wireFormat16).deserialize], - [Opcode.XOR_8, Xor.as(Xor.wireFormat8).deserialize], - [Opcode.XOR_16, Xor.as(Xor.wireFormat16).deserialize], - [Opcode.NOT_8, Not.as(Not.wireFormat8).deserialize], - [Opcode.NOT_16, Not.as(Not.wireFormat16).deserialize], - [Opcode.SHL_8, Shl.as(Shl.wireFormat8).deserialize], - [Opcode.SHL_16, Shl.as(Shl.wireFormat16).deserialize], - [Opcode.SHR_8, Shr.as(Shr.wireFormat8).deserialize], - [Opcode.SHR_16, Shr.as(Shr.wireFormat16).deserialize], - [Opcode.CAST_8, Cast.as(Cast.wireFormat8).deserialize], - [Opcode.CAST_16, Cast.as(Cast.wireFormat16).deserialize], - // Execution Environment - [Opcode.GETENVVAR_16, GetEnvVar.as(GetEnvVar.wireFormat16).deserialize], - [CalldataCopy.opcode, Instruction.deserialize.bind(CalldataCopy)], - [Opcode.RETURNDATASIZE, Instruction.deserialize.bind(ReturndataSize)], - [Opcode.RETURNDATACOPY, Instruction.deserialize.bind(ReturndataCopy)], - - // Machine State - Internal Control Flow - [Jump.opcode, Instruction.deserialize.bind(Jump)], - [JumpI.opcode, Instruction.deserialize.bind(JumpI)], - [InternalCall.opcode, Instruction.deserialize.bind(InternalCall)], - [InternalReturn.opcode, Instruction.deserialize.bind(InternalReturn)], - [Opcode.SET_8, Set.as(Set.wireFormat8).deserialize], - [Opcode.SET_16, Set.as(Set.wireFormat16).deserialize], - [Opcode.SET_32, Set.as(Set.wireFormat32).deserialize], - [Opcode.SET_64, Set.as(Set.wireFormat64).deserialize], - [Opcode.SET_128, Set.as(Set.wireFormat128).deserialize], - [Opcode.SET_FF, Set.as(Set.wireFormatFF).deserialize], - [Opcode.MOV_8, Mov.as(Mov.wireFormat8).deserialize], - [Opcode.MOV_16, Mov.as(Mov.wireFormat16).deserialize], - - // World State - [SLoad.opcode, Instruction.deserialize.bind(SLoad)], // Public Storage - [SStore.opcode, Instruction.deserialize.bind(SStore)], // Public Storage - [NoteHashExists.opcode, Instruction.deserialize.bind(NoteHashExists)], // Notes & Nullifiers - [EmitNoteHash.opcode, Instruction.deserialize.bind(EmitNoteHash)], // Notes & Nullifiers - [NullifierExists.opcode, Instruction.deserialize.bind(NullifierExists)], // Notes & Nullifiers - [EmitNullifier.opcode, Instruction.deserialize.bind(EmitNullifier)], // Notes & Nullifiers - [L1ToL2MessageExists.opcode, Instruction.deserialize.bind(L1ToL2MessageExists)], // Messages - - // Accrued Substate - [EmitUnencryptedLog.opcode, Instruction.deserialize.bind(EmitUnencryptedLog)], - [SendL2ToL1Message.opcode, Instruction.deserialize.bind(SendL2ToL1Message)], - [GetContractInstance.opcode, Instruction.deserialize.bind(GetContractInstance)], - - // Control Flow - Contract Calls - [Call.opcode, Instruction.deserialize.bind(Call)], - [StaticCall.opcode, Instruction.deserialize.bind(StaticCall)], - [Return.opcode, Instruction.deserialize.bind(Return)], - [Opcode.REVERT_8, Revert.as(Revert.wireFormat8).deserialize], - [Opcode.REVERT_16, Revert.as(Revert.wireFormat16).deserialize], - - // Misc - [DebugLog.opcode, Instruction.deserialize.bind(DebugLog)], - - // Gadgets - [EcAdd.opcode, Instruction.deserialize.bind(EcAdd)], - [Poseidon2.opcode, Instruction.deserialize.bind(Poseidon2)], - [Sha256Compression.opcode, Instruction.deserialize.bind(Sha256Compression)], - [KeccakF1600.opcode, Instruction.deserialize.bind(KeccakF1600)], - [MultiScalarMul.opcode, Instruction.deserialize.bind(MultiScalarMul)], - - // Conversions - [ToRadixBE.opcode, Instruction.deserialize.bind(ToRadixBE)], - ]); +export const INSTRUCTION_SET = new Map([ + [Opcode.ADD_8, Add.as(Add.wireFormat8).deserialize], + [Opcode.ADD_16, Add.as(Add.wireFormat16).deserialize], + [Opcode.SUB_8, Sub.as(Sub.wireFormat8).deserialize], + [Opcode.SUB_16, Sub.as(Sub.wireFormat16).deserialize], + [Opcode.MUL_8, Mul.as(Mul.wireFormat8).deserialize], + [Opcode.MUL_16, Mul.as(Mul.wireFormat16).deserialize], + [Opcode.DIV_8, Div.as(Div.wireFormat8).deserialize], + [Opcode.DIV_16, Div.as(Div.wireFormat16).deserialize], + [Opcode.FDIV_8, FieldDiv.as(FieldDiv.wireFormat8).deserialize], + [Opcode.FDIV_16, FieldDiv.as(FieldDiv.wireFormat16).deserialize], + [Opcode.EQ_8, Eq.as(Eq.wireFormat8).deserialize], + [Opcode.EQ_16, Eq.as(Eq.wireFormat16).deserialize], + [Opcode.LT_8, Lt.as(Lt.wireFormat8).deserialize], + [Opcode.LT_16, Lt.as(Lt.wireFormat16).deserialize], + [Opcode.LTE_8, Lte.as(Lte.wireFormat8).deserialize], + [Opcode.LTE_16, Lte.as(Lte.wireFormat16).deserialize], + [Opcode.AND_8, And.as(And.wireFormat8).deserialize], + [Opcode.AND_16, And.as(And.wireFormat16).deserialize], + [Opcode.OR_8, Or.as(Or.wireFormat8).deserialize], + [Opcode.OR_16, Or.as(Or.wireFormat16).deserialize], + [Opcode.XOR_8, Xor.as(Xor.wireFormat8).deserialize], + [Opcode.XOR_16, Xor.as(Xor.wireFormat16).deserialize], + [Opcode.NOT_8, Not.as(Not.wireFormat8).deserialize], + [Opcode.NOT_16, Not.as(Not.wireFormat16).deserialize], + [Opcode.SHL_8, Shl.as(Shl.wireFormat8).deserialize], + [Opcode.SHL_16, Shl.as(Shl.wireFormat16).deserialize], + [Opcode.SHR_8, Shr.as(Shr.wireFormat8).deserialize], + [Opcode.SHR_16, Shr.as(Shr.wireFormat16).deserialize], + [Opcode.CAST_8, Cast.as(Cast.wireFormat8).deserialize], + [Opcode.CAST_16, Cast.as(Cast.wireFormat16).deserialize], + // Execution Environment + [Opcode.GETENVVAR_16, GetEnvVar.as(GetEnvVar.wireFormat16).deserialize], + [CalldataCopy.opcode, Instruction.deserialize.bind(CalldataCopy)], + [Opcode.RETURNDATASIZE, Instruction.deserialize.bind(ReturndataSize)], + [Opcode.RETURNDATACOPY, Instruction.deserialize.bind(ReturndataCopy)], + + // Machine State - Internal Control Flow + [Jump.opcode, Instruction.deserialize.bind(Jump)], + [JumpI.opcode, Instruction.deserialize.bind(JumpI)], + [InternalCall.opcode, Instruction.deserialize.bind(InternalCall)], + [InternalReturn.opcode, Instruction.deserialize.bind(InternalReturn)], + [Opcode.SET_8, Set.as(Set.wireFormat8).deserialize], + [Opcode.SET_16, Set.as(Set.wireFormat16).deserialize], + [Opcode.SET_32, Set.as(Set.wireFormat32).deserialize], + [Opcode.SET_64, Set.as(Set.wireFormat64).deserialize], + [Opcode.SET_128, Set.as(Set.wireFormat128).deserialize], + [Opcode.SET_FF, Set.as(Set.wireFormatFF).deserialize], + [Opcode.MOV_8, Mov.as(Mov.wireFormat8).deserialize], + [Opcode.MOV_16, Mov.as(Mov.wireFormat16).deserialize], + + // World State + [SLoad.opcode, Instruction.deserialize.bind(SLoad)], // Public Storage + [SStore.opcode, Instruction.deserialize.bind(SStore)], // Public Storage + [NoteHashExists.opcode, Instruction.deserialize.bind(NoteHashExists)], // Notes & Nullifiers + [EmitNoteHash.opcode, Instruction.deserialize.bind(EmitNoteHash)], // Notes & Nullifiers + [NullifierExists.opcode, Instruction.deserialize.bind(NullifierExists)], // Notes & Nullifiers + [EmitNullifier.opcode, Instruction.deserialize.bind(EmitNullifier)], // Notes & Nullifiers + [L1ToL2MessageExists.opcode, Instruction.deserialize.bind(L1ToL2MessageExists)], // Messages + + // Accrued Substate + [EmitUnencryptedLog.opcode, Instruction.deserialize.bind(EmitUnencryptedLog)], + [SendL2ToL1Message.opcode, Instruction.deserialize.bind(SendL2ToL1Message)], + [GetContractInstance.opcode, Instruction.deserialize.bind(GetContractInstance)], + + // Control Flow - Contract Calls + [Call.opcode, Instruction.deserialize.bind(Call)], + [StaticCall.opcode, Instruction.deserialize.bind(StaticCall)], + [Return.opcode, Instruction.deserialize.bind(Return)], + [Opcode.REVERT_8, Revert.as(Revert.wireFormat8).deserialize], + [Opcode.REVERT_16, Revert.as(Revert.wireFormat16).deserialize], + + // Misc + [DebugLog.opcode, Instruction.deserialize.bind(DebugLog)], + + // Gadgets + [EcAdd.opcode, Instruction.deserialize.bind(EcAdd)], + [Poseidon2.opcode, Instruction.deserialize.bind(Poseidon2)], + [Sha256Compression.opcode, Instruction.deserialize.bind(Sha256Compression)], + [KeccakF1600.opcode, Instruction.deserialize.bind(KeccakF1600)], + [MultiScalarMul.opcode, Instruction.deserialize.bind(MultiScalarMul)], + + // Conversions + [ToRadixBE.opcode, Instruction.deserialize.bind(ToRadixBE)], +]); /** * Serializes an array of instructions to bytecode. @@ -159,10 +156,7 @@ export function encodeToBytecode(instructions: Serializable[]): Buffer { } // For testing only -export function decodeFromBytecode( - bytecode: Buffer, - instructionSet: InstructionSet = INSTRUCTION_SET(), -): Instruction[] { +export function decodeFromBytecode(bytecode: Buffer, instructionSet: InstructionSet = INSTRUCTION_SET): Instruction[] { const instructions: Instruction[] = []; let pc = 0; while (pc < bytecode.length) { @@ -177,7 +171,7 @@ export function decodeFromBytecode( export function decodeInstructionFromBytecode( bytecode: Buffer, pc: number, - instructionSet: InstructionSet = INSTRUCTION_SET(), + instructionSet: InstructionSet = INSTRUCTION_SET, ): [Instruction, number] { if (pc >= bytecode.length) { throw new InvalidProgramCounterError(pc, bytecode.length);