Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions yarn-project/simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand All @@ -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<AvmSimulator>;

/**
* Prepare a new AVM context that will be ready for an external/nested call
* - Fork the world state journal
Expand Down
5 changes: 4 additions & 1 deletion yarn-project/simulator/src/avm/avm_simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.`,
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/simulator/src/avm/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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. */
Expand Down
3 changes: 1 addition & 2 deletions yarn-project/simulator/src/avm/opcodes/external_calls.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;

Expand Down
180 changes: 87 additions & 93 deletions yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,94 +62,91 @@ export interface Deserializable {
}

export type InstructionSet = Map<Opcode, InstructionDeserializer>;
// 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, InstructionDeserializer>([
[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, InstructionDeserializer>([
[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.
Expand All @@ -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) {
Expand All @@ -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);
Expand Down