From 42b8c12cb89a2193767fbf08c07954219d1d7a26 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 17:19:05 +0000 Subject: [PATCH 01/16] chore(avm-simulator): refactor of AVM Simulator to more closely match YP --- .../acir-simulator/src/avm/avm_context.ts | 207 ++++++++++----- .../src/avm/avm_execution_environment.test.ts | 4 +- .../src/avm/avm_execution_environment.ts | 4 +- .../src/avm/avm_machine_state.ts | 88 ++++--- .../src/avm/avm_message_call_result.ts | 32 +-- .../acir-simulator/src/avm/fixtures/index.ts | 16 +- .../acir-simulator/src/avm/index.test.ts | 72 ++++-- .../src/avm/interpreter/interpreter.test.ts | 43 +++- .../src/avm/interpreter/interpreter.ts | 140 +++++----- .../src/avm/opcodes/accrued_substate.test.ts | 45 ++-- .../src/avm/opcodes/accrued_substate.ts | 43 ++-- .../src/avm/opcodes/arithmetic.test.ts | 62 ++--- .../src/avm/opcodes/arithmetic.ts | 43 ++-- .../src/avm/opcodes/bitwise.test.ts | 101 ++++---- .../acir-simulator/src/avm/opcodes/bitwise.ts | 73 +++--- .../src/avm/opcodes/comparators.test.ts | 60 +++-- .../src/avm/opcodes/comparators.ts | 39 ++- .../src/avm/opcodes/control_flow.test.ts | 88 ++++--- .../src/avm/opcodes/control_flow.ts | 44 ++-- .../avm/opcodes/environment_getters.test.ts | 30 ++- .../src/avm/opcodes/environment_getters.ts | 13 +- .../src/avm/opcodes/external_calls.test.ts | 36 +-- .../src/avm/opcodes/external_calls.ts | 64 ++--- .../src/avm/opcodes/instruction.ts | 22 +- .../src/avm/opcodes/memory.test.ts | 240 ++++++++---------- .../acir-simulator/src/avm/opcodes/memory.ts | 45 ++-- .../src/avm/opcodes/storage.test.ts | 43 ++-- .../acir-simulator/src/avm/opcodes/storage.ts | 29 +-- 28 files changed, 954 insertions(+), 772 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index b6226c818e9..3ef693e3fcf 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -2,73 +2,152 @@ import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { AvmExecutionEnvironment } from './avm_execution_environment.js'; -import { AvmMachineState } from './avm_machine_state.js'; -import { AvmMessageCallResult } from './avm_message_call_result.js'; -import { AvmInterpreterError, executeAvm } from './interpreter/index.js'; +import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; import { AvmJournal } from './journal/journal.js'; -import { Instruction } from './opcodes/instruction.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; +import { PublicExecutionResult } from '../index.js'; +import { InstructionExecutionError, type Instruction } from './opcodes/index.js'; +import { AvmContractCallResults } from './avm_message_call_result.js'; +import { assert } from 'console'; +import { createDebugLogger } from '@aztec/foundation/log'; -// FIXME: dependency cycle. +export type AvmContextInputs = { + environment: AvmExecutionEnvironment, + initialMachineState: InitialAvmMachineState, +} /** - * Avm Executor manages the execution of the AVM - * - * It stores a state manager + * Avm Context manages the state and execution of the AVM */ export class AvmContext { /** Contains constant variables provided by the kernel */ - private executionEnvironment: AvmExecutionEnvironment; + public environment: AvmExecutionEnvironment; + /** VM state that is modified on an instruction-by-instruction basis */ + public machineState: AvmMachineState; + //public results: AvmContractCallResults = { reverted: false, output: []}; + /** Manages mutable state during execution - (caching, fetching) */ - private journal: AvmJournal; + public journal: AvmJournal; + + /** The public contract code corresponding to this context's contract class */ + private instructions: Instruction[]; + + /** Stage data for public kernel (1-kernel-per-call). + * Shouldn't be necessary once kernel processes an entire AVM Session. */ + //private nestedExecutions: PublicExecutionResult[] = []; - constructor(executionEnvironment: AvmExecutionEnvironment, journal: AvmJournal) { - this.executionEnvironment = executionEnvironment; + constructor( + // TODO: just accept environment and initial machine state as separate inputs + // TODO: add AvmSession that accepts the equivalent of "circuit inputs" + contextInputs: AvmContextInputs, + journal: AvmJournal, + private log = createDebugLogger('aztec:avm_simulator:avm_context'), + ) { + this.environment = contextInputs.environment; + this.machineState = new AvmMachineState(contextInputs.initialMachineState); this.journal = journal; + // Bytecode is fetched and instructions are decoded in async init() + this.instructions = []; } - /** - * Call a contract with the given calldata - * - * - We get the contract from storage - * - We interpret the bytecode - * - We run the interpreter - * - */ - async call(): Promise { + async init() { // NOTE: the following is mocked as getPublicBytecode does not exist yet const selector = new FunctionSelector(0); const bytecode = await this.journal.hostStorage.contractsDb.getBytecode( - this.executionEnvironment.address, + this.environment.address, selector, ); // This assumes that we will not be able to send messages to accounts without code // Pending classes and instances impl details if (!bytecode) { - throw new NoBytecodeFoundInterpreterError(this.executionEnvironment.address); + throw new NoBytecodeFoundInterpreterError(this.environment.address); } - const instructions: Instruction[] = decodeFromBytecode(bytecode); + this.instructions = decodeFromBytecode(bytecode); + } - const machineState = new AvmMachineState(this.executionEnvironment); - return executeAvm(machineState, this.journal, instructions); + /** + * For testing purposes (to skip bytecode decoding) + */ + setInstructions(instructions: Instruction[]) { + this.instructions = instructions; } /** - * Create a new forked avm context - for internal calls + * Execute the contract code within the current context. + * + * - Retrieve and decode bytecode + * - Interpret the bytecode + * - Execute + * + */ + async execute(): Promise { + // Cannot execute empty contract or uninitialized context + assert(this.instructions.length > 0); + + try { + while (!this.machineState.halted) { + const instruction = this.instructions[this.machineState.pc]; + assert(!!instruction); // This should never happen + + this.log(`Executing PC=${this.machineState.pc}: ${instruction.toString()}`); + await instruction.execute(this); + + if (this.machineState.pc >= this.instructions.length) { + this.log('Passed end of program!'); + throw new InvalidProgramCounterError(this.machineState.pc, /*max=*/ this.instructions.length); + } + } + + // return results for processing by calling context + const results = this.machineState.getResults(); + this.log(`Context execution results: ${results.toString()}`); + return results; + } catch (e) { + this.log('Exceptional halt'); + if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { + this.log(`Unknown error thrown by avm: ${e}`); + throw e; + } + + // Exceptional halts cannot return data + const results = new AvmContractCallResults(/*reverted=*/ true, /*output*/ [], /*revertReason=*/ e); + this.log(`Context execution results: ${results.toString()}`); + return results; + } + } + + /** + * Merge the journal of this call with it's parent + * NOTE: this should never be called on a root context - only from within a nested call + */ + mergeJournalSuccess() { + this.journal.mergeSuccessWithParent(); + } + + /** + * Merge the journal of this call with it's parent + * For when the child call fails ( we still must track state accesses ) */ - public newWithForkedState(): AvmContext { - const forkedState = AvmJournal.branchParent(this.journal); - return new AvmContext(this.executionEnvironment, forkedState); + mergeJournalFailure() { + this.journal.mergeFailureWithParent(); } + /** + * Create a new forked avm context - for internal calls + */ + //public newWithForkedState(): AvmContext { + // const forkedState = AvmJournal.branchParent(this.journal); + // return new AvmContext(this.environment, forkedState); + //} + /** * Create a new forked avm context - for external calls */ - public static newWithForkedState(executionEnvironment: AvmExecutionEnvironment, journal: AvmJournal): AvmContext { + public static newWithForkedState(contextInputs: AvmContextInputs, journal: AvmJournal): AvmContext { const forkedState = AvmJournal.branchParent(journal); - return new AvmContext(executionEnvironment, forkedState); + return new AvmContext(contextInputs, forkedState); } /** @@ -78,19 +157,23 @@ export class AvmContext { * - Alter both address and storageAddress * * @param address - The contract to call - * @param executionEnvironment - The current execution environment + * @param parentEnvironment - The current execution environment * @param journal - The current journal * @returns new AvmContext instance */ - public static prepExternalCallContext( + public static async createNestedContractCallContext( address: AztecAddress, calldata: Fr[], - executionEnvironment: AvmExecutionEnvironment, + parentEnvironment: AvmExecutionEnvironment, + initialMachineState: InitialAvmMachineState, journal: AvmJournal, - ): AvmContext { - const newExecutionEnvironment = executionEnvironment.newCall(address, calldata); + ): Promise { + const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedCall(address, calldata); + const newContextInputs = { environment: newExecutionEnvironment, initialMachineState }; const forkedState = AvmJournal.branchParent(journal); - return new AvmContext(newExecutionEnvironment, forkedState); + const nestedContext = new AvmContext(newContextInputs, forkedState); + await nestedContext.init(); + return nestedContext; } /** @@ -100,35 +183,34 @@ export class AvmContext { * - Alter both address and storageAddress * * @param address - The contract to call - * @param executionEnvironment - The current execution environment + * @param parentEnvironment - The current execution environment * @param journal - The current journal * @returns new AvmContext instance */ - public static prepExternalStaticCallContext( + public static async createNestedStaticCallContext( address: AztecAddress, calldata: Fr[], - executionEnvironment: AvmExecutionEnvironment, + parentEnvironment: AvmExecutionEnvironment, + initialMachineState: InitialAvmMachineState, journal: AvmJournal, - ): AvmContext { - const newExecutionEnvironment = executionEnvironment.newStaticCall(address, calldata); + ): Promise { + const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedStaticCall(address, calldata); + const newContextInputs: AvmContextInputs = { environment: newExecutionEnvironment, initialMachineState }; const forkedState = AvmJournal.branchParent(journal); - return new AvmContext(newExecutionEnvironment, forkedState); + const nestedContext = new AvmContext(newContextInputs, forkedState); + await nestedContext.init(); + return nestedContext; } +} - /** - * Merge the journal of this call with it's parent - * NOTE: this should never be called on a root context - only from within a nested call - */ - public mergeJournalSuccess() { - this.journal.mergeSuccessWithParent(); - } - /** - * Merge the journal of this call with it's parent - * For when the child call fails ( we still must track state accesses ) - */ - public mergeJournalFailure() { - this.journal.mergeFailureWithParent(); +/** + * Avm-specific errors should derive from this + */ +export abstract class AvmInterpreterError extends Error { + constructor(message: string, ...rest: any[]) { + super(message, ...rest); + this.name = 'AvmInterpreterError'; } } @@ -138,3 +220,14 @@ class NoBytecodeFoundInterpreterError extends AvmInterpreterError { this.name = 'NoBytecodeFoundInterpreterError'; } } + +/** + * Error is thrown when the program counter goes to an invalid location. + * There is no instruction at the provided pc + */ +export class InvalidProgramCounterError extends AvmInterpreterError { + constructor(pc: number, max: number) { + super(`Invalid program counter ${pc}, max is ${max}`); + this.name = 'InvalidProgramCounterError'; + } +} diff --git a/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts b/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts index bf792862c8d..27043eb3660 100644 --- a/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts @@ -8,7 +8,7 @@ describe('Execution Environment', () => { it('New call should fork execution environment correctly', () => { const executionEnvironment = initExecutionEnvironment(); - const newExecutionEnvironment = executionEnvironment.newCall(newAddress, calldata); + const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedCall(newAddress, calldata); allTheSameExcept(executionEnvironment, newExecutionEnvironment, { address: newAddress, @@ -30,7 +30,7 @@ describe('Execution Environment', () => { it('New static call call should fork execution environment correctly', () => { const executionEnvironment = initExecutionEnvironment(); - const newExecutionEnvironment = executionEnvironment.newStaticCall(newAddress, calldata); + const newExecutionEnvironment = executionEnvironment.deriveEnvironmentForNestedStaticCall(newAddress, calldata); allTheSameExcept(executionEnvironment, newExecutionEnvironment, { address: newAddress, diff --git a/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts b/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts index 6a87f62d87b..aa3bbb4672c 100644 --- a/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts +++ b/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts @@ -37,7 +37,7 @@ export class AvmExecutionEnvironment { public readonly calldata: Fr[], ) {} - public newCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { + public deriveEnvironmentForNestedCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { return new AvmExecutionEnvironment( /*address=*/ address, /*storageAddress=*/ address, @@ -55,7 +55,7 @@ export class AvmExecutionEnvironment { ); } - public newStaticCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { + public deriveEnvironmentForNestedStaticCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { return new AvmExecutionEnvironment( /*address=*/ address, /*storageAddress=*/ address, diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 75da070f6b5..3244e41e7c2 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -1,69 +1,91 @@ -import { Fr } from '@aztec/foundation/fields'; +//import { Fr } from '@aztec/foundation/fields'; -import { AvmExecutionEnvironment } from './avm_execution_environment.js'; +//import { AvmExecutionEnvironment } from './avm_execution_environment.js'; +import { Fr } from '@aztec/circuits.js'; import { TaggedMemory } from './avm_memory_types.js'; +import { AvmContractCallResults } from './avm_message_call_result.js'; + +export type InitialAvmMachineState = { + l1GasLeft: number, + l2GasLeft: number, + daGasLeft: number, +} /** * Store's data for an Avm execution frame */ export class AvmMachineState { - /** - * Execution environment contains hard coded information that is received from the kernel - * Items like, the block header and global variables fall within this category - */ - public readonly executionEnvironment: AvmExecutionEnvironment; + ///** + // * Execution environment contains hard coded information that is received from the kernel + // * Items like, the block header and global variables fall within this category + // */ + //public readonly executionEnvironment: AvmExecutionEnvironment; - private returnData: Fr[]; - public readonly memory: TaggedMemory; + public l1GasLeft: number; + public l2GasLeft: number; + public daGasLeft: number; + + public pc: number = 0; /** * When an internal_call is invoked, the internal call stack is added to with the current pc + 1 * When internal_return is invoked, the latest value is popped from the internal call stack and set to the pc. */ - public internalCallStack: number[]; - - public pc: number; + public internalCallStack: number[] = []; - public callStack: number[]; + public readonly memory: TaggedMemory = new TaggedMemory(); /** * If an instruction triggers a halt, then it ends execution of the VM */ - public halted: boolean; + public halted: boolean = false; /** * Signifies if the execution has reverted ( due to a revert instruction ) */ - public reverted: boolean; + private reverted: boolean = false; /** - * Create a new avm context - * @param executionEnvironment - Machine context that is passed to the avm + * Output data must NOT be modified once it is set */ - constructor(executionEnvironment: AvmExecutionEnvironment) { - this.returnData = []; - this.memory = new TaggedMemory(); - this.internalCallStack = []; + private output: Fr[] = []; - this.pc = 0; - this.callStack = []; + /** + * Create a new avm context + * @param initialMachineState - The initial machine state passed to the avm + */ + constructor(initialMachineState: InitialAvmMachineState) { + this.l1GasLeft = initialMachineState.l1GasLeft; + this.l2GasLeft = initialMachineState.l2GasLeft; + this.daGasLeft = initialMachineState.daGasLeft; + } - this.halted = false; - this.reverted = false; + public incrementPc() { + this.pc++; + } - this.executionEnvironment = executionEnvironment; + /** + * Halt as successful + * Output data must NOT be modified once it is set + * @param output + */ + public return(output: Fr[]) { + this.halted = true; + this.output = output; } /** - * Return data must NOT be modified once it is set - * @param returnData - + * Halt as reverted + * Output data must NOT be modified once it is set + * @param output */ - public setReturnData(returnData: Fr[]) { - this.returnData = returnData; - Object.freeze(returnData); + public revert(output: Fr[]) { + this.halted = true; + this.reverted = true; + this.output = output; } - public getReturnData(): Fr[] { - return this.returnData; + public getResults(): AvmContractCallResults { + return new AvmContractCallResults(this.reverted, this.output); } } diff --git a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts index 713a306b3e9..2003374d632 100644 --- a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts +++ b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts @@ -1,37 +1,25 @@ import { Fr } from '@aztec/foundation/fields'; /** - * AVM message call result. + * AVM contract call results. */ -export class AvmMessageCallResult { +export class AvmContractCallResults { public readonly reverted: boolean; + public readonly output: Fr[]; public readonly revertReason: Error | undefined; - /** .- */ - public readonly output: Fr[]; - private constructor(reverted: boolean, output: Fr[], revertReason?: Error) { + constructor(reverted: boolean, output: Fr[], revertReason?: Error) { this.reverted = reverted; this.output = output; this.revertReason = revertReason; } - /** - * Terminate a call as a success - * @param output - Return data - * @returns instance of AvmMessageCallResult - */ - public static success(output: Fr[]): AvmMessageCallResult { - return new AvmMessageCallResult(false, output); - } - - /** - * Terminate a call as a revert - * @param output - Return data ( revert message ) - * @param reason - Optional reason for revert - * @returns instance of AvmMessageCallResult - */ - public static revert(output: Fr[], reason?: Error): AvmMessageCallResult { - return new AvmMessageCallResult(true, output, reason); + toString(): string { + let resultsStr = `reverted: ${this.reverted}, output: ${this.output}`; + if (this.revertReason) { + resultsStr += `, revertReason: ${this.revertReason}`; + } + return resultsStr; } } diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index 4c0d889063c..142290248fd 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -5,9 +5,10 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; +import { AvmMachineState } from '../avm_machine_state.js'; /** - * Create an empty instance of the Execution Environment where all values are zero, unless overriden in the overrides object + * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object */ export function initExecutionEnvironment(overrides?: Partial): AvmExecutionEnvironment { return new AvmExecutionEnvironment( @@ -28,7 +29,7 @@ export function initExecutionEnvironment(overrides?: Partial): GlobalVariables { return new GlobalVariables( @@ -38,3 +39,14 @@ export function initGlobalVariables(overrides?: Partial): Globa overrides?.timestamp ?? Fr.zero(), ); } + +/** + * Create an empty instance of the Machine State where all values are zero, unless overridden in the overrides object + */ +export function initMachineState(overrides?: Partial): AvmMachineState { + return new AvmMachineState({ + l1GasLeft: overrides?.l1GasLeft ?? 0, + l2GasLeft: overrides?.l2GasLeft ?? 0, + daGasLeft: overrides?.daGasLeft ?? 0, + }); +} \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index a9e05a97ea2..97cc0d5c3c1 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -1,20 +1,33 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts'; -import { mock } from 'jest-mock-extended'; +import { jest } from '@jest/globals'; +import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from './avm_machine_state.js'; import { TypeTag } from './avm_memory_types.js'; -import { initExecutionEnvironment } from './fixtures/index.js'; -import { executeAvm } from './interpreter/interpreter.js'; +import { AvmContext, AvmContextInputs } from './avm_context.js'; +import { initExecutionEnvironment, initMachineState } from './fixtures/index.js'; +import { HostStorage } from './journal/host_storage.js'; import { AvmJournal } from './journal/journal.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; -import { decodeFromBytecode, encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; describe('avm', () => { + let journal: AvmJournal; + let contractsDb: MockProxy; + + beforeEach(() => { + contractsDb = mock(); + + const commitmentsDb = mock(); + const publicStateDb = mock(); + const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); + journal = new AvmJournal(hostStorage); + }); + it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; - const journal = mock(); // Construct bytecode const bytecode = encodeToBytecode([ @@ -23,16 +36,24 @@ describe('avm', () => { new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), ]); - // Decode bytecode into instructions - const instructions = decodeFromBytecode(bytecode); + jest + .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(bytecode)); - // Execute instructions - const context = new AvmMachineState(initExecutionEnvironment({ calldata })); - const avmReturnData = await executeAvm(context, journal, instructions); + // Initialize AVM context + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + const context = new AvmContext(contextInputs, journal); + await context.init(); - expect(avmReturnData.reverted).toBe(false); + // Execute AVM + const results = await context.execute(); - const returnData = avmReturnData.output; + expect(results.reverted).toBe(false); + + const returnData = results.output; expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); @@ -41,22 +62,31 @@ describe('avm', () => { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/4361): sync wire format w/transpiler. it('Should execute contract function that performs addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; - const journal = mock(); // Get contract function artifact const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!; // Decode bytecode into instructions - const instructionsBytecode = Buffer.from(addArtifact.bytecode, 'base64'); - const instructions = decodeFromBytecode(instructionsBytecode); + const bytecode = Buffer.from(addArtifact.bytecode, 'base64'); + + jest + .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(bytecode)); + + // Initialize AVM context + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + const context = new AvmContext(contextInputs, journal); + await context.init(); - // Execute instructions - const context = new AvmMachineState(initExecutionEnvironment({ calldata })); - const avmReturnData = await executeAvm(context, journal, instructions); + // Execute AVM + const results = await context.execute(); - expect(avmReturnData.reverted).toBe(false); + expect(results.reverted).toBe(false); - const returnData = avmReturnData.output; + const returnData = results.output; expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts index c0111f067ac..8d858e3f7bc 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts @@ -2,21 +2,26 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext, InvalidProgramCounterError } from '../avm_context.js'; import { TypeTag } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Add } from '../opcodes/arithmetic.js'; import { Jump, Return } from '../opcodes/control_flow.js'; import { Instruction } from '../opcodes/instruction.js'; import { CalldataCopy } from '../opcodes/memory.js'; -import { InvalidProgramCounterError, executeAvm } from './interpreter.js'; describe('interpreter', () => { + let context: AvmContext; let journal: MockProxy; beforeEach(() => { journal = mock(); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); it('Should execute a series of instructions', async () => { @@ -28,12 +33,18 @@ describe('interpreter', () => { new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), ]; - const machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); - const avmReturnData = await executeAvm(machineState, journal, instructions); + const contextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) + // Set instructions (skip bytecode decoding) + context.setInstructions(instructions); + const results = await context.execute() - expect(avmReturnData.reverted).toBe(false); - expect(avmReturnData.revertReason).toBeUndefined(); - expect(avmReturnData.output).toEqual([new Fr(3)]); + expect(results.reverted).toBe(false); + expect(results.revertReason).toBeUndefined(); + expect(results.output).toEqual([new Fr(3)]); }); it('Should revert with an invalid jump', async () => { @@ -43,11 +54,17 @@ describe('interpreter', () => { const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; - const machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); - const avmReturnData = await executeAvm(machineState, journal, instructions); + const contextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) + // Set instructions (skip bytecode decoding) + context.setInstructions(instructions); + const results = await context.execute() - expect(avmReturnData.reverted).toBe(true); - expect(avmReturnData.revertReason).toBeInstanceOf(InvalidProgramCounterError); - expect(avmReturnData.output).toHaveLength(0); + expect(results.reverted).toBe(true); + expect(results.revertReason).toBeInstanceOf(InvalidProgramCounterError); + expect(results.output).toHaveLength(0); }); }); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts index fb23425f8b8..5e4cb843eb9 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts @@ -1,68 +1,72 @@ -import { strict as assert } from 'assert'; - -import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmMessageCallResult } from '../avm_message_call_result.js'; -import { AvmJournal } from '../journal/index.js'; -import { Instruction, InstructionExecutionError } from '../opcodes/instruction.js'; - -/** - * Run the avm - * @returns bool - successful execution will return true - * - reverted execution will return false - * - any other panic will throw - */ -export async function executeAvm( - machineState: AvmMachineState, - journal: AvmJournal, - instructions: Instruction[] = [], -): Promise { - assert(instructions.length > 0); - - try { - while (!machineState.halted) { - const instruction = instructions[machineState.pc]; - assert(!!instruction); // This should never happen - - await instruction.execute(machineState, journal); - - if (machineState.pc >= instructions.length) { - throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length); - } - } - - const returnData = machineState.getReturnData(); - if (machineState.reverted) { - return AvmMessageCallResult.revert(returnData); - } - - return AvmMessageCallResult.success(returnData); - } catch (e) { - if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { - throw e; - } - - const revertData = machineState.getReturnData(); - return AvmMessageCallResult.revert(revertData, /*revertReason=*/ e); - } -} - -/** - * Avm-specific errors should derive from this - */ -export abstract class AvmInterpreterError extends Error { - constructor(message: string, ...rest: any[]) { - super(message, ...rest); - this.name = 'AvmInterpreterError'; - } -} - -/** - * Error is thrown when the program counter goes to an invalid location. - * There is no instruction at the provided pc - */ -export class InvalidProgramCounterError extends AvmInterpreterError { - constructor(pc: number, max: number) { - super(`Invalid program counter ${pc}, max is ${max}`); - this.name = 'InvalidProgramCounterError'; - } -} +//import { strict as assert } from 'assert'; +// +//import { AvmContractCallResults } from '../avm_message_call_result.js'; +//import { Instruction, InstructionExecutionError } from '../opcodes/instruction.js'; +//import { AvmContext } from '../avm_context.js'; +// +// +////export class AvmSessionExecutor { +// /** +// * Run the avm +// * @returns bool - successful execution will return true +// * - reverted execution will return false +// * - any other panic will throw +// */ +// export async function executeAvm( +// context: AvmContext, +// instructions: Instruction[] = [], +// ): Promise { +// assert(instructions.length > 0); +// // TODO unclear the separation between AvmContext and this +// +// try { +// while (!context.machineState.halted) { +// const instruction = instructions[context.machineState.pc]; +// assert(!!instruction); // This should never happen +// +// await instruction.execute(context); +// +// if (context.machineState.pc >= instructions.length) { +// throw new InvalidProgramCounterError(context.machineState.pc, /*max=*/ instructions.length); +// } +// } +// +// const returnData = context.results.output; +// if (context.results.reverted) { +// return AvmContractCallResults.revert(returnData); +// } +// +// return AvmContractCallResults.success(returnData); +// } catch (e) { +// if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { +// throw e; +// } +// +// // reverts can return data as well +// const returnData = context.results.output; +// return AvmContractCallResults.revert(returnData, /*revertReason=*/ e); +// } +// } +////} +// +///** +// * Avm-specific errors should derive from this +// */ +//export abstract class AvmInterpreterError extends Error { +// constructor(message: string, ...rest: any[]) { +// super(message, ...rest); +// this.name = 'AvmInterpreterError'; +// } +//} +// +///** +// * Error is thrown when the program counter goes to an invalid location. +// * There is no instruction at the provided pc +// */ +//export class InvalidProgramCounterError extends AvmInterpreterError { +// constructor(pc: number, max: number) { +// super(`Invalid program counter ${pc}, max is ${max}`); +// this.name = 'InvalidProgramCounterError'; +// } +//} +// \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index 0163b8bea21..9e8152e5215 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -1,21 +1,25 @@ import { mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmContext } from '../avm_context.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmJournal } from '../journal/journal.js'; import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; import { StaticCallStorageAlterError } from './storage.js'; describe('Accrued Substate', () => { + let context: AvmContext; let journal: AvmJournal; - let machineState: AvmMachineState; beforeEach(() => { const hostStorage = mock(); journal = new AvmJournal(hostStorage); - machineState = new AvmMachineState(initExecutionEnvironment()); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('EmitNoteHash', () => { @@ -33,11 +37,11 @@ describe('Accrued Substate', () => { it('Should append a new note hash correctly', async () => { const value = new Field(69n); - machineState.memory.set(0, value); + context.machineState.memory.set(0, value); - await new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0).execute(machineState, journal); + await new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0).execute(context); - const journalState = journal.flush(); + const journalState = context.journal.flush(); const expected = [value.toFr()]; expect(journalState.newNoteHashes).toEqual(expected); }); @@ -58,11 +62,11 @@ describe('Accrued Substate', () => { it('Should append a new nullifier correctly', async () => { const value = new Field(69n); - machineState.memory.set(0, value); + context.machineState.memory.set(0, value); - await new EmitNullifier(/*indirect=*/ 0, /*offset=*/ 0).execute(machineState, journal); + await new EmitNullifier(/*indirect=*/ 0, /*offset=*/ 0).execute(context); - const journalState = journal.flush(); + const journalState = context.journal.flush(); const expected = [value.toFr()]; expect(journalState.newNullifiers).toEqual(expected); }); @@ -86,13 +90,13 @@ describe('Accrued Substate', () => { const startOffset = 0; const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; - machineState.memory.setSlice(0, values); + context.machineState.memory.setSlice(0, values); const length = values.length; - await new EmitUnencryptedLog(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(machineState, journal); + await new EmitUnencryptedLog(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(context); - const journalState = journal.flush(); + const journalState = context.journal.flush(); const expected = values.map(v => v.toFr()); expect(journalState.newLogs).toEqual([expected]); }); @@ -116,21 +120,24 @@ describe('Accrued Substate', () => { const startOffset = 0; const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; - machineState.memory.setSlice(0, values); + context.machineState.memory.setSlice(0, values); const length = values.length; - await new SendL2ToL1Message(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(machineState, journal); + await new SendL2ToL1Message(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(context); - const journalState = journal.flush(); + const journalState = context.journal.flush(); const expected = values.map(v => v.toFr()); expect(journalState.newL1Messages).toEqual([expected]); }); }); it('All substate instructions should fail within a static call', async () => { - const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); - machineState = new AvmMachineState(executionEnvironment); + const contextInputs = { + environment: initExecutionEnvironment({ isStaticCall: true }), + initialMachineState: { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0 }, + }; + context = new AvmContext(contextInputs, journal) const instructions = [ new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0), @@ -140,7 +147,7 @@ describe('Accrued Substate', () => { ]; for (const instruction of instructions) { - await expect(instruction.execute(machineState, journal)).rejects.toThrow(StaticCallStorageAlterError); + await expect(instruction.execute(context)).rejects.toThrow(StaticCallStorageAlterError); } }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index 1e85dde5d20..b6a762ceac7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -1,5 +1,4 @@ -import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmJournal } from '../journal/journal.js'; +import type { AvmContext } from '../avm_context.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { StaticCallStorageAlterError } from './storage.js'; @@ -14,15 +13,15 @@ export class EmitNoteHash extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - if (machineState.executionEnvironment.isStaticCall) { + async execute(context: AvmContext): Promise { + if (context.environment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const noteHash = machineState.memory.get(this.noteHashOffset).toFr(); - journal.writeNoteHash(noteHash); + const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr(); + context.journal.writeNoteHash(noteHash); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -36,15 +35,15 @@ export class EmitNullifier extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - if (machineState.executionEnvironment.isStaticCall) { + async execute(context: AvmContext): Promise { + if (context.environment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const nullifier = machineState.memory.get(this.nullifierOffset).toFr(); - journal.writeNullifier(nullifier); + const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr(); + context.journal.writeNullifier(nullifier); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -58,15 +57,15 @@ export class EmitUnencryptedLog extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - if (machineState.executionEnvironment.isStaticCall) { + async execute(context: AvmContext): Promise { + if (context.environment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const log = machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr()); - journal.writeLog(log); + const log = context.machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr()); + context.journal.writeLog(log); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -80,14 +79,14 @@ export class SendL2ToL1Message extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - if (machineState.executionEnvironment.isStaticCall) { + async execute(context: AvmContext): Promise { + if (context.environment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const msg = machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr()); - journal.writeL1Message(msg); + const msg = context.machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr()); + context.journal.writeL1Message(msg); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index f54db2d82a7..e178ec446f6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,18 +1,22 @@ import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext } from '../avm_context.js'; import { Field, TypeTag } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: MockProxy; - beforeEach(async () => { - machineState = new AvmMachineState(initExecutionEnvironment()); + beforeEach(() => { journal = mock(); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('Add', () => { @@ -41,8 +45,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(1n); const b = new Field(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Add( /*indirect=*/ 0, @@ -50,10 +54,10 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); const expected = new Field(3n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -61,8 +65,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(1n); const b = new Field(Field.MODULUS - 1n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Add( /*indirect=*/ 0, @@ -70,10 +74,10 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); const expected = new Field(0n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -104,8 +108,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(1n); const b = new Field(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Sub( /*indirect=*/ 0, @@ -113,10 +117,10 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); const expected = new Field(Field.MODULUS - 1n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -147,8 +151,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(2n); const b = new Field(3n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Mul( /*indirect=*/ 0, @@ -156,10 +160,10 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); const expected = new Field(6n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -167,8 +171,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(2n); const b = new Field(Field.MODULUS / 2n - 1n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Mul( /*indirect=*/ 0, @@ -176,10 +180,10 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); const expected = new Field(Field.MODULUS - 3n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -210,8 +214,8 @@ describe('Arithmetic Instructions', () => { const a = new Field(2n); const b = new Field(3n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Div( /*indirect=*/ 0, @@ -219,9 +223,9 @@ describe('Arithmetic Instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); const recovered = actual.mul(b); expect(recovered).toEqual(a); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 71a571b45dd..91c5a44a6dc 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,5 +1,4 @@ -import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmJournal } from '../journal/index.js'; +import type { AvmContext } from '../avm_context.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; @@ -11,14 +10,14 @@ export class Add extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); const dest = a.add(b); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -30,14 +29,14 @@ export class Sub extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); const dest = a.sub(b); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -49,14 +48,14 @@ export class Mul extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); const dest = a.mul(b); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -68,13 +67,13 @@ export class Div extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); const dest = a.div(b); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index d41c34611e0..243074a034a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -1,18 +1,22 @@ import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext } from '../avm_context.js'; import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; describe('Bitwise instructions', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: MockProxy; beforeEach(async () => { - machineState = new AvmMachineState(initExecutionEnvironment()); journal = mock(); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('AND', () => { @@ -38,8 +42,8 @@ describe('Bitwise instructions', () => { }); it('Should AND correctly over integral types', async () => { - machineState.memory.set(0, new Uint32(0b11111110010011100100n)); - machineState.memory.set(1, new Uint32(0b11100100111001001111n)); + context.machineState.memory.set(0, new Uint32(0b11111110010011100100n)); + context.machineState.memory.set(1, new Uint32(0b11100100111001001111n)); await new And( /*indirect=*/ 0, @@ -47,9 +51,9 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(new Uint32(0b11100100010001000100n)); }); }); @@ -80,8 +84,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0b11100100111001001111n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Or( /*indirect=*/ 0, @@ -89,10 +93,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint32(0b11111110111011101111n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -123,8 +127,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0b11100100111001001111n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Xor( /*indirect=*/ 0, @@ -132,10 +136,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint32(0b00011010101010101011n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -166,8 +170,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shr( /*indirect=*/ 0, @@ -175,10 +179,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = a; - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -186,8 +190,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shr( /*indirect=*/ 0, @@ -195,10 +199,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint32(0b00111111100100111001n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -206,8 +210,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(19n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shr( /*indirect=*/ 0, @@ -215,10 +219,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint32(0b01n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -249,8 +253,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shl( /*indirect=*/ 0, @@ -258,10 +262,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = a; - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -269,8 +273,8 @@ describe('Bitwise instructions', () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shl( /*indirect=*/ 0, @@ -278,10 +282,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint32(0b1111111001001110010000n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -289,8 +293,8 @@ describe('Bitwise instructions', () => { const a = new Uint16(0b1110010011100111n); const b = new Uint16(17n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shl( /*indirect=*/ 0, @@ -298,10 +302,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint16(0n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -309,8 +313,8 @@ describe('Bitwise instructions', () => { const a = new Uint16(0b1110010011100111n); const b = new Uint16(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); await new Shl( /*indirect=*/ 0, @@ -318,10 +322,10 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(machineState, journal); + ).execute(context) const expected = new Uint16(0b1001001110011100n); - const actual = machineState.memory.get(2); + const actual = context.machineState.memory.get(2); expect(actual).toEqual(expected); }); }); @@ -349,15 +353,12 @@ describe('Bitwise instructions', () => { it('Should NOT correctly over integral types', async () => { const a = new Uint16(0b0110010011100100n); - machineState.memory.set(0, a); + context.machineState.memory.set(0, a); - await new Not(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute( - machineState, - journal, - ); + await new Not(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute(context); const expected = new Uint16(0b1001101100011011n); // high bits! - const actual = machineState.memory.get(1); + const actual = context.machineState.memory.get(1); expect(actual).toEqual(expected); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index f30be9b5053..36ef547396a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,6 +1,5 @@ -import { AvmMachineState } from '../avm_machine_state.js'; +import type { AvmContext } from '../avm_context.js'; import { IntegralValue } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/index.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js'; @@ -13,16 +12,16 @@ export class And extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); const res = a.and(b); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -34,16 +33,16 @@ export class Or extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); const res = a.or(b); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -55,16 +54,16 @@ export class Xor extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); const res = a.xor(b); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -76,15 +75,15 @@ export class Not extends TwoOperandInstruction { super(indirect, inTag, aOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset); - const a = machineState.memory.getAs(this.aOffset); + const a = context.machineState.memory.getAs(this.aOffset); const res = a.not(); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -96,16 +95,16 @@ export class Shl extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); const res = a.shl(b); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -117,15 +116,15 @@ export class Shr extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); const res = a.shr(b); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index b707b138aa7..7bf793970c7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -1,19 +1,23 @@ import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Eq, Lt, Lte } from './comparators.js'; import { InstructionExecutionError } from './instruction.js'; describe('Comparators', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: MockProxy; beforeEach(async () => { - machineState = new AvmMachineState(initExecutionEnvironment()); journal = mock(); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('Eq', () => { @@ -39,33 +43,33 @@ describe('Comparators', () => { }); it('Works on integral types', async () => { - machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); + context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); [ new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Uint32(0), new Uint32(0), new Uint32(1)]); }); it('Works on field elements', async () => { - machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); [ new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Field(0), new Field(0), new Field(1)]); }); it('InTag is checked', async () => { - machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), @@ -75,7 +79,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); } }); }); @@ -103,33 +107,33 @@ describe('Comparators', () => { }); it('Works on integral types', async () => { - machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); [ new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Uint32(0), new Uint32(1), new Uint32(0)]); }); it('Works on field elements', async () => { - machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); [ new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Field(0), new Field(1), new Field(0)]); }); it('InTag is checked', async () => { - machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), @@ -139,7 +143,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); } }); }); @@ -167,33 +171,33 @@ describe('Comparators', () => { }); it('Works on integral types', async () => { - machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); [ new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Uint32(1), new Uint32(1), new Uint32(0)]); }); it('Works on field elements', async () => { - machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); [ new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); expect(actual).toEqual([new Field(1), new Field(1), new Field(0)]); }); it('InTag is checked', async () => { - machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), @@ -203,7 +207,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); } }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index a4cffa19d4a..2ad5ecd0e7e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,5 +1,4 @@ -import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmJournal } from '../journal/index.js'; +import type { AvmContext } from '../avm_context.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; @@ -12,17 +11,17 @@ export class Eq extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.equals(b) ? 1n : 0n); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -34,17 +33,17 @@ export class Lt extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.lt(b) ? 1n : 0n); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -56,16 +55,16 @@ export class Lte extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + async execute(context: AvmContext): Promise { + Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.equals(b) || a.lt(b) ? 1n : 0n); - machineState.memory.set(this.dstOffset, dest); + context.machineState.memory.set(this.dstOffset, dest); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 82879c619b9..30589f11d8a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -2,20 +2,24 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; import { InstructionExecutionError } from './instruction.js'; describe('Control Flow Opcodes', () => { + let context: AvmContext; let journal: MockProxy; - let machineState: AvmMachineState; beforeEach(() => { journal = mock(); - machineState = new AvmMachineState(initExecutionEnvironment()); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('JUMP', () => { @@ -33,11 +37,11 @@ describe('Control Flow Opcodes', () => { it('Should implement JUMP', async () => { const jumpLocation = 22; - expect(machineState.pc).toBe(0); + expect(context.machineState.pc).toBe(0); const instruction = new Jump(jumpLocation); - await instruction.execute(machineState, journal); - expect(machineState.pc).toBe(jumpLocation); + await instruction.execute(context); + expect(context.machineState.pc).toBe(jumpLocation); }); }); @@ -59,31 +63,31 @@ describe('Control Flow Opcodes', () => { const jumpLocation = 22; const jumpLocation1 = 69; - expect(machineState.pc).toBe(0); + expect(context.machineState.pc).toBe(0); - machineState.memory.set(0, new Uint16(1n)); - machineState.memory.set(1, new Uint16(2n)); + context.machineState.memory.set(0, new Uint16(1n)); + context.machineState.memory.set(1, new Uint16(2n)); const instruction = new JumpI(/*indirect=*/ 0, jumpLocation, /*condOffset=*/ 0); - await instruction.execute(machineState, journal); - expect(machineState.pc).toBe(jumpLocation); + await instruction.execute(context); + expect(context.machineState.pc).toBe(jumpLocation); // Truthy can be greater than 1 const instruction1 = new JumpI(/*indirect=*/ 0, jumpLocation1, /*condOffset=*/ 1); - await instruction1.execute(machineState, journal); - expect(machineState.pc).toBe(jumpLocation1); + await instruction1.execute(context); + expect(context.machineState.pc).toBe(jumpLocation1); }); it('Should implement JUMPI - falsy', async () => { const jumpLocation = 22; - expect(machineState.pc).toBe(0); + expect(context.machineState.pc).toBe(0); - machineState.memory.set(0, new Uint16(0n)); + context.machineState.memory.set(0, new Uint16(0n)); const instruction = new JumpI(/*indirect=*/ 0, jumpLocation, /*condOffset=*/ 0); - await instruction.execute(machineState, journal); - expect(machineState.pc).toBe(1); + await instruction.execute(context); + expect(context.machineState.pc).toBe(1); }); }); @@ -102,20 +106,20 @@ describe('Control Flow Opcodes', () => { it('Should implement Internal Call and Return', async () => { const jumpLocation = 22; - expect(machineState.pc).toBe(0); + expect(context.machineState.pc).toBe(0); const instruction = new InternalCall(jumpLocation); const returnInstruction = new InternalReturn(); - await instruction.execute(machineState, journal); - expect(machineState.pc).toBe(jumpLocation); + await instruction.execute(context); + expect(context.machineState.pc).toBe(jumpLocation); - await returnInstruction.execute(machineState, journal); - expect(machineState.pc).toBe(1); + await returnInstruction.execute(context); + expect(context.machineState.pc).toBe(1); }); it('Should error if Internal Return is called without a corresponding Internal Call', async () => { - const returnInstruction = () => new InternalReturn().execute(machineState, journal); + const returnInstruction = () => new InternalReturn().execute(context); await expect(returnInstruction()).rejects.toThrow(InstructionExecutionError); }); }); @@ -151,8 +155,8 @@ describe('Control Flow Opcodes', () => { ]; for (let i = 0; i < instructions.length; i++) { - await instructions[i].execute(machineState, journal); - expect(machineState.pc).toBe(expectedPcs[i]); + await instructions[i].execute(context); + expect(context.machineState.pc).toBe(expectedPcs[i]); } }); }); @@ -174,16 +178,18 @@ describe('Control Flow Opcodes', () => { it('Should return data from the return opcode', async () => { const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState.memory.set(0, new Field(1n)); - machineState.memory.set(1, new Field(2n)); - machineState.memory.set(2, new Field(3n)); + context.machineState.memory.set(0, new Field(1n)); + context.machineState.memory.set(1, new Field(2n)); + context.machineState.memory.set(2, new Field(3n)); const instruction = new Return(/*indirect=*/ 0, /*returnOffset=*/ 0, returnData.length); - await instruction.execute(machineState, journal); + await instruction.execute(context); - expect(machineState.getReturnData()).toEqual(returnData); - expect(machineState.halted).toBe(true); - expect(machineState.reverted).toBe(false); + expect(context.machineState.halted).toBe(true); + expect(context.machineState.getResults()).toEqual({ + reverted: false, + output: returnData, + }); }); }); @@ -204,16 +210,18 @@ describe('Control Flow Opcodes', () => { it('Should return data and revert from the revert opcode', async () => { const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState.memory.set(0, new Field(1n)); - machineState.memory.set(1, new Field(2n)); - machineState.memory.set(2, new Field(3n)); + context.machineState.memory.set(0, new Field(1n)); + context.machineState.memory.set(1, new Field(2n)); + context.machineState.memory.set(2, new Field(3n)); const instruction = new Revert(/*indirect=*/ 0, /*returnOffset=*/ 0, returnData.length); - await instruction.execute(machineState, journal); + await instruction.execute(context); - expect(machineState.getReturnData()).toEqual(returnData); - expect(machineState.halted).toBe(true); - expect(machineState.reverted).toBe(true); + expect(context.machineState.halted).toBe(true); + expect(context.machineState.getResults()).toEqual({ + reverted: true, + output: returnData, + }); }); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 1527a677116..3afdc7281a9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,6 +1,5 @@ -import { AvmMachineState } from '../avm_machine_state.js'; +import type { AvmContext } from '../avm_context.js'; import { IntegralValue } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/journal.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; @@ -19,12 +18,12 @@ export class Return extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); - - machineState.setReturnData(returnData); + async execute(context: AvmContext): Promise { + const output = context.machineState.memory + .getSlice(this.returnOffset, this.copySize) + .map(word => word.toFr()); - this.halt(machineState); + context.machineState.return(output); } } @@ -43,13 +42,12 @@ export class Revert extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const returnData = machineState.memory + async execute(context: AvmContext): Promise { + const output = context.machineState.memory .getSlice(this.returnOffset, this.returnOffset + this.retSize) .map(word => word.toFr()); - machineState.setReturnData(returnData); - this.revert(machineState); + context.machineState.revert(output); } } @@ -63,8 +61,8 @@ export class Jump extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - machineState.pc = this.jumpOffset; + async execute(context: AvmContext): Promise { + context.machineState.pc = this.jumpOffset; } } @@ -84,14 +82,14 @@ export class JumpI extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const condition = machineState.memory.getAs(this.condOffset); + async execute(context: AvmContext): Promise { + const condition = context.machineState.memory.getAs(this.condOffset); // TODO: reconsider this casting if (condition.toBigInt() == 0n) { - this.incrementPc(machineState); + context.machineState.incrementPc(); } else { - machineState.pc = this.loc; + context.machineState.pc = this.loc; } } } @@ -106,9 +104,9 @@ export class InternalCall extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - machineState.internalCallStack.push(machineState.pc + 1); - machineState.pc = this.loc; + async execute(context: AvmContext): Promise { + context.machineState.internalCallStack.push(context.machineState.pc + 1); + context.machineState.pc = this.loc; } } @@ -122,11 +120,11 @@ export class InternalReturn extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const jumpOffset = machineState.internalCallStack.pop(); + async execute(context: AvmContext): Promise { + const jumpOffset = context.machineState.internalCallStack.pop(); if (jumpOffset === undefined) { throw new InstructionExecutionError('Internal call empty!'); } - machineState.pc = jumpOffset; + context.machineState.pc = jumpOffset; } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 2acf4729546..04dbe651599 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -2,8 +2,8 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; -import { initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; +import { AvmContext, AvmContextInputs } from '../avm_context.js'; +import { initExecutionEnvironment, initGlobalVariables, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Address, @@ -21,19 +21,23 @@ import { } from './environment_getters.js'; describe('Environment getters instructions', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: MockProxy; - beforeEach(async () => { + beforeEach(() => { journal = mock(); }); type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; const envGetterTest = async (key: string, value: Fr, instruction: EnvInstruction) => { - machineState = new AvmMachineState(initExecutionEnvironment({ [key]: value })); + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ [key]: value }), + initialMachineState: initMachineState() + }; + context = new AvmContext(contextInputs, journal); - await instruction.execute(machineState, journal); - const actual = machineState.memory.get(0).toFr(); + await instruction.execute(context); + const actual = context.machineState.memory.get(0).toFr(); expect(actual).toEqual(value); }; @@ -193,10 +197,14 @@ describe('Environment getters instructions', () => { type GlobalsInstruction = ChainId | Version | BlockNumber | Timestamp; const readGlobalVariableTest = async (key: string, value: Fr, instruction: GlobalsInstruction) => { const globals = initGlobalVariables({ [key]: value }); - machineState = new AvmMachineState(initExecutionEnvironment({ globals })); - - await instruction.execute(machineState, journal); - const actual = machineState.memory.get(0).toFr(); + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ globals }), + initialMachineState: initMachineState() + }; + context = new AvmContext(contextInputs, journal); + + await instruction.execute(context); + const actual = context.machineState.memory.get(0).toFr(); expect(actual).toEqual(value); }; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index 8dc59330e94..35e2e29c4d9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -1,7 +1,6 @@ -import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; -import { AvmMachineState } from '../avm_machine_state.js'; +import type { AvmContext } from '../avm_context.js'; +import type { AvmExecutionEnvironment } from '../avm_execution_environment.js'; import { Field } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/journal.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -13,10 +12,10 @@ abstract class GetterInstruction extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const res = new Field(this.getIt(machineState.executionEnvironment)); - machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + async execute(context: AvmContext): Promise { + const res = new Field(this.getIt(context.environment)); + context.machineState.memory.set(this.dstOffset, res); + context.machineState.incrementPc(); } protected abstract getIt(env: AvmExecutionEnvironment): any; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index 2395cc32479..72a9cc0fec8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -4,9 +4,9 @@ import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmJournal } from '../journal/journal.js'; import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; @@ -17,20 +17,24 @@ import { CalldataCopy } from './memory.js'; import { SStore } from './storage.js'; describe('External Calls', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: AvmJournal; let contractsDb: MockProxy; beforeEach(() => { - machineState = new AvmMachineState(initExecutionEnvironment()); - contractsDb = mock(); const commitmentsDb = mock(); const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); journal = new AvmJournal(hostStorage); + + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('Call', () => { @@ -79,9 +83,9 @@ describe('External Calls', () => { new Return(/*indirect=*/ 0, /*retOffset=*/ 0, /*size=*/ 2), ]); - machineState.memory.set(0, new Field(gas)); - machineState.memory.set(1, new Field(addr)); - machineState.memory.setSlice(2, args); + context.machineState.memory.set(0, new Field(gas)); + context.machineState.memory.set(1, new Field(addr)); + context.machineState.memory.setSlice(2, args); jest .spyOn(journal.hostStorage.contractsDb, 'getBytecode') .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); @@ -96,12 +100,12 @@ describe('External Calls', () => { retSize, successOffset, ); - await instruction.execute(machineState, journal); + await instruction.execute(context); - const successValue = machineState.memory.get(successOffset); + const successValue = context.machineState.memory.get(successOffset); expect(successValue).toEqual(new Field(1n)); - const retValue = machineState.memory.getSlice(retOffset, retSize); + const retValue = context.machineState.memory.getSlice(retOffset, retSize); expect(retValue).toEqual([new Field(1n), new Field(2n)]); // Check that the storage call has been merged into the parent journal @@ -158,9 +162,9 @@ describe('External Calls', () => { const retSize = 2; const successOffset = 7; - machineState.memory.set(0, gas); - machineState.memory.set(1, addr); - machineState.memory.setSlice(2, args); + context.machineState.memory.set(0, gas); + context.machineState.memory.set(1, addr); + context.machineState.memory.setSlice(2, args); const otherContextInstructions: Instruction[] = [ new CalldataCopy(/*indirect=*/ 0, /*csOffset=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0), @@ -183,10 +187,10 @@ describe('External Calls', () => { retSize, successOffset, ); - await instruction.execute(machineState, journal); + await instruction.execute(context); // No revert has occurred, but the nested execution has failed - const successValue = machineState.memory.get(successOffset); + const successValue = context.machineState.memory.get(successOffset); expect(successValue).toEqual(new Field(0n)); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 79b96891f72..5a202f67256 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -1,12 +1,11 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmContext } from '../avm_context.js'; -import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/journal.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; + export class Call extends Instruction { static type: string = 'CALL'; static readonly opcode: Opcode = Opcode.CALL; @@ -37,34 +36,36 @@ export class Call extends Instruction { } // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - const callAddress = machineState.memory.getAs(this.addrOffset); - const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); + async execute(context: AvmContext): Promise { + const callAddress = context.machineState.memory.getAs(this.addrOffset); + const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); - const avmContext = AvmContext.prepExternalCallContext( + const nestedContext = await AvmContext.createNestedContractCallContext( new Fr(callAddress.toBigInt()), calldata, - machineState.executionEnvironment, - journal, + context.environment, + { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0}, + context.journal, ); - const returnObject = await avmContext.call(); - const success = !returnObject.reverted; + const nestedCallResults = await nestedContext.execute(); + const success = !nestedCallResults.reverted; // We only take as much data as was specified in the return size -> TODO: should we be reverting here - const returnData = returnObject.output.slice(0, this.retSize); + const returnData = nestedCallResults.output.slice(0, this.retSize); const convertedReturnData = returnData.map(f => new Field(f)); // Write our return data into memory - machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); - machineState.memory.setSlice(this.retOffset, convertedReturnData); + context.machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); + context.machineState.memory.setSlice(this.retOffset, convertedReturnData); if (success) { - avmContext.mergeJournalSuccess(); + nestedContext.mergeJournalSuccess(); } else { - avmContext.mergeJournalFailure(); + nestedContext.mergeJournalFailure(); } - this.incrementPc(machineState); + + context.machineState.incrementPc(); } } @@ -97,34 +98,35 @@ export class StaticCall extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - const callAddress = machineState.memory.get(this.addrOffset); - const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); + async execute(context: AvmContext): Promise { + const callAddress = context.machineState.memory.get(this.addrOffset); + const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); - const avmContext = AvmContext.prepExternalStaticCallContext( + const nestedContext = await AvmContext.createNestedStaticCallContext( new Fr(callAddress.toBigInt()), calldata, - machineState.executionEnvironment, - journal, + context.environment, + { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0}, + context.journal, ); - const returnObject = await avmContext.call(); - const success = !returnObject.reverted; + const nestedCallResults = await nestedContext.execute(); + const success = !nestedCallResults.reverted; // We only take as much data as was specified in the return size -> TODO: should we be reverting here - const returnData = returnObject.output.slice(0, this.retSize); + const returnData = nestedCallResults.output.slice(0, this.retSize); const convertedReturnData = returnData.map(f => new Field(f)); // Write our return data into memory - machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); - machineState.memory.setSlice(this.retOffset, convertedReturnData); + context.machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); + context.machineState.memory.setSlice(this.retOffset, convertedReturnData); if (success) { - avmContext.mergeJournalSuccess(); + nestedContext.mergeJournalSuccess(); } else { - avmContext.mergeJournalFailure(); + nestedContext.mergeJournalFailure(); } - this.incrementPc(machineState); + context.machineState.incrementPc(); } -} +} \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index ff5fdd5e96f..4a6c3b9cd5b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -2,29 +2,29 @@ import { assert } from 'console'; import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/index.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; +import type { AvmContext } from '../avm_context.js'; /** * Parent class for all AVM instructions. * It's most important aspects are execution and (de)serialization. */ export abstract class Instruction { - public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; + static readonly type: string | undefined; - incrementPc(machineState: AvmMachineState): void { - machineState.pc++; + public static getName(): string { + return this.type ? this.type: 'INVALID INSTRUCTION'; } + public toString(): string { + let instructionStr = this.constructor.name + ': '; + for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { + instructionStr += `${prop}:${this[prop].toString()}, `; - halt(machineState: AvmMachineState): void { - machineState.halted = true; - } - - revert(machineState: AvmMachineState): void { - machineState.halted = true; - machineState.reverted = true; + } + return instructionStr; } + public abstract execute(context: AvmContext): Promise; static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { for (const offset of offsets) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 438e7d9eadb..0ebc83d14fb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -2,20 +2,24 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext, AvmContextInputs } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Memory instructions', () => { - let machineState: AvmMachineState; + let context: AvmContext; let journal: MockProxy; beforeEach(async () => { - machineState = new AvmMachineState(initExecutionEnvironment()); journal = mock(); + const contextInputs = { + environment: initExecutionEnvironment(), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('SET', () => { @@ -39,41 +43,32 @@ describe('Memory instructions', () => { }); it('should correctly set value and tag (uninitialized)', async () => { - await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*value=*/ 1234n, /*offset=*/ 1).execute( - machineState, - journal, - ); + await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*value=*/ 1234n, /*offset=*/ 1).execute(context); - const actual = machineState.memory.get(1); - const tag = machineState.memory.getTag(1); + const actual = context.machineState.memory.get(1); + const tag = context.machineState.memory.getTag(1); expect(actual).toEqual(new Uint16(1234n)); expect(tag).toEqual(TypeTag.UINT16); }); it('should correctly set value and tag (overwriting)', async () => { - machineState.memory.set(1, new Field(27)); + context.machineState.memory.set(1, new Field(27)); - await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT32, /*value=*/ 1234n, /*offset=*/ 1).execute( - machineState, - journal, - ); + await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT32, /*value=*/ 1234n, /*offset=*/ 1).execute(context); - const actual = machineState.memory.get(1); - const tag = machineState.memory.getTag(1); + const actual = context.machineState.memory.get(1); + const tag = context.machineState.memory.getTag(1); expect(actual).toEqual(new Uint32(1234n)); expect(tag).toEqual(TypeTag.UINT32); }); it('should correctly set value and tag (truncating)', async () => { - await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*value=*/ 0x12345678n, /*offset=*/ 1).execute( - machineState, - journal, - ); + await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*value=*/ 0x12345678n, /*offset=*/ 1).execute(context); - const actual = machineState.memory.get(1); - const tag = machineState.memory.getTag(1); + const actual = context.machineState.memory.get(1); + const tag = context.machineState.memory.getTag(1); expect(actual).toEqual(new Uint16(0x5678)); expect(tag).toEqual(TypeTag.UINT16); @@ -82,7 +77,7 @@ describe('Memory instructions', () => { it('should throw if tag is FIELD, UNINITIALIZED, INVALID', async () => { for (const tag of [TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID]) { await expect( - new Set(/*indirect=*/ 0, /*inTag=*/ tag, /*value=*/ 1234n, /*offset=*/ 1).execute(machineState, journal), + new Set(/*indirect=*/ 0, /*inTag=*/ tag, /*value=*/ 1234n, /*offset=*/ 1).execute(context), ).rejects.toThrow(InstructionExecutionError); } }); @@ -109,11 +104,11 @@ describe('Memory instructions', () => { }); it('Should upcast between integral types', () => { - machineState.memory.set(0, new Uint8(20n)); - machineState.memory.set(1, new Uint16(65000n)); - machineState.memory.set(2, new Uint32(1n << 30n)); - machineState.memory.set(3, new Uint64(1n << 50n)); - machineState.memory.set(4, new Uint128(1n << 100n)); + context.machineState.memory.set(0, new Uint8(20n)); + context.machineState.memory.set(1, new Uint16(65000n)); + context.machineState.memory.set(2, new Uint32(1n << 30n)); + context.machineState.memory.set(3, new Uint64(1n << 50n)); + context.machineState.memory.set(4, new Uint128(1n << 100n)); [ new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 10), @@ -121,9 +116,9 @@ describe('Memory instructions', () => { new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 2, /*dstOffset=*/ 12), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 3, /*dstOffset=*/ 13), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); expect(actual).toEqual([ new Uint16(20n), new Uint32(65000n), @@ -131,16 +126,16 @@ describe('Memory instructions', () => { new Uint128(1n << 50n), new Uint128(1n << 100n), ]); - const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + const tags = context.machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); expect(tags).toEqual([TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128, TypeTag.UINT128]); }); it('Should downcast (truncating) between integral types', () => { - machineState.memory.set(0, new Uint8(20n)); - machineState.memory.set(1, new Uint16(65000n)); - machineState.memory.set(2, new Uint32((1n << 30n) - 1n)); - machineState.memory.set(3, new Uint64((1n << 50n) - 1n)); - machineState.memory.set(4, new Uint128((1n << 100n) - 1n)); + context.machineState.memory.set(0, new Uint8(20n)); + context.machineState.memory.set(1, new Uint16(65000n)); + context.machineState.memory.set(2, new Uint32((1n << 30n) - 1n)); + context.machineState.memory.set(3, new Uint64((1n << 50n) - 1n)); + context.machineState.memory.set(4, new Uint128((1n << 100n) - 1n)); [ new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), @@ -148,9 +143,9 @@ describe('Memory instructions', () => { new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT16, /*aOffset=*/ 2, /*dstOffset=*/ 12), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT32, /*aOffset=*/ 3, /*dstOffset=*/ 13), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 4, /*dstOffset=*/ 14), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); expect(actual).toEqual([ new Uint8(20n), new Uint8(232), @@ -158,26 +153,25 @@ describe('Memory instructions', () => { new Uint32((1n << 32n) - 1n), new Uint64((1n << 64n) - 1n), ]); - const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + const tags = context.machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64]); }); it('Should upcast from integral types to field', () => { - machineState.memory.set(0, new Uint8(20n)); - machineState.memory.set(1, new Uint16(65000n)); - machineState.memory.set(2, new Uint32(1n << 30n)); - machineState.memory.set(3, new Uint64(1n << 50n)); - machineState.memory.set(4, new Uint128(1n << 100n)); - + context.machineState.memory.set(0, new Uint8(20n)); + context.machineState.memory.set(1, new Uint16(65000n)); + context.machineState.memory.set(2, new Uint32(1n << 30n)); + context.machineState.memory.set(3, new Uint64(1n << 50n)); + context.machineState.memory.set(4, new Uint128(1n << 100n)); [ new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 10), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 1, /*dstOffset=*/ 11), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 2, /*dstOffset=*/ 12), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 3, /*dstOffset=*/ 13), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 4, /*dstOffset=*/ 14), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); expect(actual).toEqual([ new Field(20n), new Field(65000n), @@ -185,16 +179,16 @@ describe('Memory instructions', () => { new Field(1n << 50n), new Field(1n << 100n), ]); - const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + const tags = context.machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); expect(tags).toEqual([TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD]); }); it('Should downcast (truncating) from field to integral types', () => { - machineState.memory.set(0, new Field((1n << 200n) - 1n)); - machineState.memory.set(1, new Field((1n << 200n) - 1n)); - machineState.memory.set(2, new Field((1n << 200n) - 1n)); - machineState.memory.set(3, new Field((1n << 200n) - 1n)); - machineState.memory.set(4, new Field((1n << 200n) - 1n)); + context.machineState.memory.set(0, new Field((1n << 200n) - 1n)); + context.machineState.memory.set(1, new Field((1n << 200n) - 1n)); + context.machineState.memory.set(2, new Field((1n << 200n) - 1n)); + context.machineState.memory.set(3, new Field((1n << 200n) - 1n)); + context.machineState.memory.set(4, new Field((1n << 200n) - 1n)); [ new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), @@ -202,9 +196,9 @@ describe('Memory instructions', () => { new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT32, /*aOffset=*/ 2, /*dstOffset=*/ 12), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 3, /*dstOffset=*/ 13), new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), - ].forEach(i => i.execute(machineState, journal)); + ].forEach(i => i.execute(context)); - const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); expect(actual).toEqual([ new Uint8((1n << 8n) - 1n), new Uint16((1n << 16n) - 1n), @@ -212,21 +206,18 @@ describe('Memory instructions', () => { new Uint64((1n << 64n) - 1n), new Uint128((1n << 128n) - 1n), ]); - const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + const tags = context.machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128]); }); it('Should cast between field elements', async () => { - machineState.memory.set(0, new Field(12345678n)); + context.machineState.memory.set(0, new Field(12345678n)); - await new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute( - machineState, - journal, - ); + await new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute(context); - const actual = machineState.memory.get(1); + const actual = context.machineState.memory.get(1); expect(actual).toEqual(new Field(12345678n)); - const tags = machineState.memory.getTag(1); + const tags = context.machineState.memory.getTag(1); expect(tags).toEqual(TypeTag.FIELD); }); }); @@ -246,22 +237,22 @@ describe('Memory instructions', () => { }); it('Should move integrals on different memory cells', async () => { - machineState.memory.set(0, new Uint16(27)); - await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); + context.machineState.memory.set(0, new Uint16(27)); + await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(context); - const actual = machineState.memory.get(1); - const tag = machineState.memory.getTag(1); + const actual = context.machineState.memory.get(1); + const tag = context.machineState.memory.getTag(1); expect(actual).toEqual(new Uint16(27n)); expect(tag).toEqual(TypeTag.UINT16); }); it('Should move field elements on different memory cells', async () => { - machineState.memory.set(0, new Field(27)); - await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); + context.machineState.memory.set(0, new Field(27)); + await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(context); - const actual = machineState.memory.get(1); - const tag = machineState.memory.getTag(1); + const actual = context.machineState.memory.get(1); + const tag = context.machineState.memory.getTag(1); expect(actual).toEqual(new Field(27n)); expect(tag).toEqual(TypeTag.FIELD); @@ -291,65 +282,53 @@ describe('Memory instructions', () => { }); it('Should move A if COND is true, on different memory cells (integral condition)', async () => { - machineState.memory.set(0, new Uint32(123)); // A - machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new Uint8(2)); // Condition + context.machineState.memory.set(0, new Uint32(123)); // A + context.machineState.memory.set(1, new Uint16(456)); // B + context.machineState.memory.set(2, new Uint8(2)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( - machineState, - journal, - ); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); - const actual = machineState.memory.get(3); - const tag = machineState.memory.getTag(3); + const actual = context.machineState.memory.get(3); + const tag = context.machineState.memory.getTag(3); expect(actual).toEqual(new Uint32(123)); expect(tag).toEqual(TypeTag.UINT32); }); it('Should move B if COND is false, on different memory cells (integral condition)', async () => { - machineState.memory.set(0, new Uint32(123)); // A - machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new Uint8(0)); // Condition + context.machineState.memory.set(0, new Uint32(123)); // A + context.machineState.memory.set(1, new Uint16(456)); // B + context.machineState.memory.set(2, new Uint8(0)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( - machineState, - journal, - ); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); - const actual = machineState.memory.get(3); - const tag = machineState.memory.getTag(3); + const actual = context.machineState.memory.get(3); + const tag = context.machineState.memory.getTag(3); expect(actual).toEqual(new Uint16(456)); expect(tag).toEqual(TypeTag.UINT16); }); it('Should move A if COND is true, on different memory cells (field condition)', async () => { - machineState.memory.set(0, new Uint32(123)); // A - machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new Field(1)); // Condition + context.machineState.memory.set(0, new Uint32(123)); // A + context.machineState.memory.set(1, new Uint16(456)); // B + context.machineState.memory.set(2, new Field(1)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( - machineState, - journal, - ); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); - const actual = machineState.memory.get(3); - const tag = machineState.memory.getTag(3); + const actual = context.machineState.memory.get(3); + const tag = context.machineState.memory.getTag(3); expect(actual).toEqual(new Uint32(123)); expect(tag).toEqual(TypeTag.UINT32); }); it('Should move B if COND is false, on different memory cells (integral condition)', async () => { - machineState.memory.set(0, new Uint32(123)); // A - machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new Field(0)); // Condition + context.machineState.memory.set(0, new Uint32(123)); // A + context.machineState.memory.set(1, new Uint16(456)); // B + context.machineState.memory.set(2, new Field(0)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( - machineState, - journal, - ); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); - const actual = machineState.memory.get(3); - const tag = machineState.memory.getTag(3); + const actual = context.machineState.memory.get(3); + const tag = context.machineState.memory.getTag(3); expect(actual).toEqual(new Uint16(456)); expect(tag).toEqual(TypeTag.UINT16); }); @@ -377,43 +356,46 @@ describe('Memory instructions', () => { it('Writes nothing if size is 0', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); - machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal); + context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute( - machineState, - journal, - ); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(context); - const actual = machineState.memory.get(0); + const actual = context.machineState.memory.get(0); expect(actual).toEqual(new Uint16(12)); }); it('Copies all calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); - machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal); + context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute( - machineState, - journal, - ); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(context); - const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); + const actual = context.machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); expect(actual).toEqual([new Field(1), new Field(2), new Field(3)]); }); it('Copies slice of calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); - machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ calldata }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal); + context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute( - machineState, - journal, - ); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(context); - const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); + const actual = context.machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); expect(actual).toEqual([new Field(2), new Field(3)]); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index fd26f92060c..ee2ca0cc0b7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,6 +1,5 @@ -import { AvmMachineState } from '../avm_machine_state.js'; +import type { AvmContext } from '../avm_context.js'; import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/index.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; import { TwoOperandInstruction } from './instruction_impl.js'; @@ -21,16 +20,16 @@ export class Set extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + async execute(context: AvmContext): Promise { // Per the YP, the tag cannot be a field. if ([TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID].includes(this.inTag)) { throw new InstructionExecutionError(`Invalid tag ${TypeTag[this.inTag]} for SET.`); } const res = TaggedMemory.integralFromTag(this.value, this.inTag); - machineState.memory.set(this.dstOffset, res); + context.machineState.memory.set(this.dstOffset, res); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -57,15 +56,15 @@ export class CMov extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); - const cond = machineState.memory.get(this.condOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); + const cond = context.machineState.memory.get(this.condOffset); // TODO: reconsider toBigInt() here - machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); + context.machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -77,16 +76,16 @@ export class Cast extends TwoOperandInstruction { super(indirect, dstTag, aOffset, dstOffset); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.aOffset); // TODO: consider not using toBigInt() const casted = this.inTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.inTag); - machineState.memory.set(this.dstOffset, casted); + context.machineState.memory.set(this.dstOffset, casted); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -105,12 +104,12 @@ export class Mov extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.srcOffset); + async execute(context: AvmContext): Promise { + const a = context.machineState.memory.get(this.srcOffset); - machineState.memory.set(this.dstOffset, a); + context.machineState.memory.set(this.dstOffset, a); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -130,12 +129,12 @@ export class CalldataCopy extends Instruction { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const transformedData = machineState.executionEnvironment.calldata + async execute(context: AvmContext): Promise { + const transformedData = context.environment.calldata .slice(this.cdOffset, this.cdOffset + this.copySize) .map(f => new Field(f)); - machineState.memory.setSlice(this.dstOffset, transformedData); + context.machineState.memory.setSlice(this.dstOffset, transformedData); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 8f704b036a3..03d64c2c712 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -3,22 +3,24 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmContext, AvmContextInputs } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { SLoad, SStore, StaticCallStorageAlterError } from './storage.js'; describe('Storage Instructions', () => { + let context: AvmContext; let journal: MockProxy; - let machineState: AvmMachineState; const address = AztecAddress.random(); - beforeEach(() => { + beforeEach(async () => { journal = mock(); - - const executionEnvironment = initExecutionEnvironment({ address, storageAddress: address }); - machineState = new AvmMachineState(executionEnvironment); + const contextInputs = { + environment: initExecutionEnvironment({ address, storageAddress: address }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal) }); describe('SSTORE', () => { @@ -39,26 +41,29 @@ describe('Storage Instructions', () => { const a = new Field(1n); const b = new Field(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); - await new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(machineState, journal); + await new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); expect(journal.writeStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt()), new Fr(b.toBigInt())); }); it('Should not be able to write to storage in a static call', async () => { - const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); - machineState = new AvmMachineState(executionEnvironment); + const contextInputs: AvmContextInputs = { + environment: initExecutionEnvironment({ isStaticCall: true }), + initialMachineState: initMachineState(), + }; + context = new AvmContext(contextInputs, journal); const a = new Field(1n); const b = new Field(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); const instruction = () => - new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(machineState, journal); + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); await expect(instruction()).rejects.toThrow(StaticCallStorageAlterError); }); }); @@ -85,14 +90,14 @@ describe('Storage Instructions', () => { const a = new Field(1n); const b = new Field(2n); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + context.machineState.memory.set(0, a); + context.machineState.memory.set(1, b); - await new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); + await new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 1).execute(context); expect(journal.readStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt())); - const actual = machineState.memory.get(1); + const actual = context.machineState.memory.get(1); expect(actual).toEqual(new Field(expectedResult)); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index 5cd9bee9ceb..5ac12ef5d06 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -1,8 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; -import { AvmMachineState } from '../avm_machine_state.js'; +import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/journal.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; @@ -28,21 +27,21 @@ export class SStore extends BaseStorageInstruction { super(indirect, srcOffset, slotOffset); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - if (machineState.executionEnvironment.isStaticCall) { + async execute(context: AvmContext): Promise { + if (context.environment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const slot = machineState.memory.get(this.aOffset); - const data = machineState.memory.get(this.bOffset); + const slot = context.machineState.memory.get(this.aOffset); + const data = context.machineState.memory.get(this.bOffset); - journal.writeStorage( - machineState.executionEnvironment.storageAddress, + context.journal.writeStorage( + context.environment.storageAddress, new Fr(slot.toBigInt()), new Fr(data.toBigInt()), ); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } @@ -54,17 +53,17 @@ export class SLoad extends BaseStorageInstruction { super(indirect, slotOffset, dstOffset); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - const slot = machineState.memory.get(this.aOffset); + async execute(context: AvmContext): Promise { + const slot = context.machineState.memory.get(this.aOffset); - const data: Fr = await journal.readStorage( - machineState.executionEnvironment.storageAddress, + const data: Fr = await context.journal.readStorage( + context.environment.storageAddress, new Fr(slot.toBigInt()), ); - machineState.memory.set(this.bOffset, new Field(data)); + context.machineState.memory.set(this.bOffset, new Field(data)); - this.incrementPc(machineState); + context.machineState.incrementPc(); } } From cceb4dc297abc7fc2359ef69515d9b189b802d21 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 18:41:43 +0000 Subject: [PATCH 02/16] redo context constructor inputs --- .../acir-simulator/src/avm/avm_context.ts | 44 ++++++++----------- .../acir-simulator/src/avm/fixtures/index.ts | 13 +++++- .../acir-simulator/src/avm/index.test.ts | 16 ++----- .../src/avm/interpreter/interpreter.test.ts | 14 ++---- .../src/avm/opcodes/accrued_substate.test.ts | 12 +---- .../src/avm/opcodes/arithmetic.test.ts | 6 +-- .../src/avm/opcodes/bitwise.test.ts | 6 +-- .../src/avm/opcodes/comparators.test.ts | 6 +-- .../src/avm/opcodes/control_flow.test.ts | 7 +-- .../avm/opcodes/environment_getters.test.ts | 14 ++---- .../src/avm/opcodes/external_calls.test.ts | 7 +-- .../src/avm/opcodes/memory.test.ts | 27 +++--------- .../src/avm/opcodes/storage.test.ts | 16 ++----- 13 files changed, 58 insertions(+), 130 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 3ef693e3fcf..90708ae35b7 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -1,33 +1,23 @@ import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; import { AvmJournal } from './journal/journal.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; -import { PublicExecutionResult } from '../index.js'; import { InstructionExecutionError, type Instruction } from './opcodes/index.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; -import { assert } from 'console'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; -export type AvmContextInputs = { - environment: AvmExecutionEnvironment, - initialMachineState: InitialAvmMachineState, -} +import { assert } from 'console'; /** * Avm Context manages the state and execution of the AVM */ export class AvmContext { - /** Contains constant variables provided by the kernel */ - public environment: AvmExecutionEnvironment; /** VM state that is modified on an instruction-by-instruction basis */ public machineState: AvmMachineState; - //public results: AvmContractCallResults = { reverted: false, output: []}; - - /** Manages mutable state during execution - (caching, fetching) */ - public journal: AvmJournal; /** The public contract code corresponding to this context's contract class */ private instructions: Instruction[]; @@ -37,14 +27,16 @@ export class AvmContext { //private nestedExecutions: PublicExecutionResult[] = []; constructor( - // TODO: just accept environment and initial machine state as separate inputs - // TODO: add AvmSession that accepts the equivalent of "circuit inputs" - contextInputs: AvmContextInputs, - journal: AvmJournal, + /** Manages mutable state during execution - (caching, fetching) */ + public journal: AvmJournal, + /** Contains constant variables provided by the kernel */ + public environment: AvmExecutionEnvironment = initExecutionEnvironment(), + /** Some initial values for machine state */ + initialMachineState: InitialAvmMachineState = initInitialMachineState(), private log = createDebugLogger('aztec:avm_simulator:avm_context'), ) { - this.environment = contextInputs.environment; - this.machineState = new AvmMachineState(contextInputs.initialMachineState); + this.environment = environment; + this.machineState = new AvmMachineState(initialMachineState); this.journal = journal; // Bytecode is fetched and instructions are decoded in async init() this.instructions = []; @@ -145,9 +137,13 @@ export class AvmContext { /** * Create a new forked avm context - for external calls */ - public static newWithForkedState(contextInputs: AvmContextInputs, journal: AvmJournal): AvmContext { + public static newWithForkedState( + environment: AvmExecutionEnvironment, + initialMachineState: InitialAvmMachineState, + journal: AvmJournal + ): AvmContext { const forkedState = AvmJournal.branchParent(journal); - return new AvmContext(contextInputs, forkedState); + return new AvmContext(forkedState); } /** @@ -169,9 +165,8 @@ export class AvmContext { journal: AvmJournal, ): Promise { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedCall(address, calldata); - const newContextInputs = { environment: newExecutionEnvironment, initialMachineState }; const forkedState = AvmJournal.branchParent(journal); - const nestedContext = new AvmContext(newContextInputs, forkedState); + const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); await nestedContext.init(); return nestedContext; } @@ -195,9 +190,8 @@ export class AvmContext { journal: AvmJournal, ): Promise { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedStaticCall(address, calldata); - const newContextInputs: AvmContextInputs = { environment: newExecutionEnvironment, initialMachineState }; const forkedState = AvmJournal.branchParent(journal); - const nestedContext = new AvmContext(newContextInputs, forkedState); + const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); await nestedContext.init(); return nestedContext; } diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index 142290248fd..e8184b11987 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -5,7 +5,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; -import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmMachineState, InitialAvmMachineState } from '../avm_machine_state.js'; /** * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object @@ -40,6 +40,17 @@ export function initGlobalVariables(overrides?: Partial): Globa ); } +/** + * Create an empty instance of the "Initial" Machine State where all values are zero, unless overridden in the overrides object + */ +export function initInitialMachineState(overrides?: Partial): InitialAvmMachineState { + return { + l1GasLeft: overrides?.l1GasLeft ?? 0, + l2GasLeft: overrides?.l2GasLeft ?? 0, + daGasLeft: overrides?.daGasLeft ?? 0, + }; +} + /** * Create an empty instance of the Machine State where all values are zero, unless overridden in the overrides object */ diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 97cc0d5c3c1..03457e01e43 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -5,8 +5,8 @@ import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; import { TypeTag } from './avm_memory_types.js'; -import { AvmContext, AvmContextInputs } from './avm_context.js'; -import { initExecutionEnvironment, initMachineState } from './fixtures/index.js'; +import { AvmContext } from './avm_context.js'; +import { initExecutionEnvironment } from './fixtures/index.js'; import { HostStorage } from './journal/host_storage.js'; import { AvmJournal } from './journal/journal.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; @@ -41,11 +41,7 @@ describe('avm', () => { .mockReturnValue(Promise.resolve(bytecode)); // Initialize AVM context - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - const context = new AvmContext(contextInputs, journal); + const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); await context.init(); // Execute AVM @@ -74,11 +70,7 @@ describe('avm', () => { .mockReturnValue(Promise.resolve(bytecode)); // Initialize AVM context - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - const context = new AvmContext(contextInputs, journal); + const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); await context.init(); // Execute AVM diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts index 8d858e3f7bc..c99345211a4 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts @@ -17,11 +17,7 @@ describe('interpreter', () => { beforeEach(() => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); it('Should execute a series of instructions', async () => { @@ -37,7 +33,7 @@ describe('interpreter', () => { environment: initExecutionEnvironment({ calldata }), initialMachineState: initMachineState(), }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); // Set instructions (skip bytecode decoding) context.setInstructions(instructions); const results = await context.execute() @@ -54,11 +50,7 @@ describe('interpreter', () => { const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; - const contextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); // Set instructions (skip bytecode decoding) context.setInstructions(instructions); const results = await context.execute() diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index 9e8152e5215..a0e93deac40 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -15,11 +15,7 @@ describe('Accrued Substate', () => { beforeEach(() => { const hostStorage = mock(); journal = new AvmJournal(hostStorage); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('EmitNoteHash', () => { @@ -133,11 +129,7 @@ describe('Accrued Substate', () => { }); it('All substate instructions should fail within a static call', async () => { - const contextInputs = { - environment: initExecutionEnvironment({ isStaticCall: true }), - initialMachineState: { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0 }, - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal, initExecutionEnvironment({ isStaticCall: true })); const instructions = [ new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0), diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index e178ec446f6..3364124f08a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -12,11 +12,7 @@ describe('Arithmetic Instructions', () => { beforeEach(() => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('Add', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 243074a034a..14edc1ac1ba 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -12,11 +12,7 @@ describe('Bitwise instructions', () => { beforeEach(async () => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('AND', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index 7bf793970c7..a800e199664 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -13,11 +13,7 @@ describe('Comparators', () => { beforeEach(async () => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('Eq', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 30589f11d8a..bacaaf78002 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -4,7 +4,6 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; import { InstructionExecutionError } from './instruction.js'; @@ -15,11 +14,7 @@ describe('Control Flow Opcodes', () => { beforeEach(() => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('JUMP', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 04dbe651599..27d083146e8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -2,7 +2,7 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmContext, AvmContextInputs } from '../avm_context.js'; +import { AvmContext } from '../avm_context.js'; import { initExecutionEnvironment, initGlobalVariables, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { @@ -30,11 +30,7 @@ describe('Environment getters instructions', () => { type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; const envGetterTest = async (key: string, value: Fr, instruction: EnvInstruction) => { - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ [key]: value }), - initialMachineState: initMachineState() - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment({ [key]: value })); await instruction.execute(context); const actual = context.machineState.memory.get(0).toFr(); @@ -197,11 +193,7 @@ describe('Environment getters instructions', () => { type GlobalsInstruction = ChainId | Version | BlockNumber | Timestamp; const readGlobalVariableTest = async (key: string, value: Fr, instruction: GlobalsInstruction) => { const globals = initGlobalVariables({ [key]: value }); - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ globals }), - initialMachineState: initMachineState() - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment({ globals })); await instruction.execute(context); const actual = context.machineState.memory.get(0).toFr(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index 72a9cc0fec8..ca1fd9b90da 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -29,12 +29,7 @@ describe('External Calls', () => { const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); journal = new AvmJournal(hostStorage); - - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('Call', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 0ebc83d14fb..4a095718a16 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -2,12 +2,13 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmContext, AvmContextInputs } from '../avm_context.js'; +import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; +import { init } from '@aztec/foundation/crypto'; describe('Memory instructions', () => { let context: AvmContext; @@ -15,11 +16,7 @@ describe('Memory instructions', () => { beforeEach(async () => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment(), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal) }); describe('SET', () => { @@ -356,11 +353,7 @@ describe('Memory instructions', () => { it('Writes nothing if size is 0', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(context); @@ -371,11 +364,7 @@ describe('Memory instructions', () => { it('Copies all calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(context); @@ -386,11 +375,7 @@ describe('Memory instructions', () => { it('Copies slice of calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(context); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 03d64c2c712..3651d1582c8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -3,9 +3,9 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; -import { AvmContext, AvmContextInputs } from '../avm_context.js'; +import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { SLoad, SStore, StaticCallStorageAlterError } from './storage.js'; @@ -16,11 +16,7 @@ describe('Storage Instructions', () => { beforeEach(async () => { journal = mock(); - const contextInputs = { - environment: initExecutionEnvironment({ address, storageAddress: address }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal) + context = new AvmContext(journal, initExecutionEnvironment({ address, storageAddress: address })); }); describe('SSTORE', () => { @@ -50,11 +46,7 @@ describe('Storage Instructions', () => { }); it('Should not be able to write to storage in a static call', async () => { - const contextInputs: AvmContextInputs = { - environment: initExecutionEnvironment({ isStaticCall: true }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(contextInputs, journal); + context = new AvmContext(journal, initExecutionEnvironment( { isStaticCall: true })); const a = new Field(1n); const b = new Field(2n); From f401019213f5b6e61aa83d0b7c8e19c77987b691 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 18:56:12 +0000 Subject: [PATCH 03/16] remove interpreter --- .../src/avm/avm_context.test.ts | 61 +++++++++++++++- .../acir-simulator/src/avm/avm_context.ts | 47 +++--------- yarn-project/acir-simulator/src/avm/errors.ts | 29 ++++++++ .../src/avm/interpreter/index.ts | 1 - .../src/avm/interpreter/interpreter.test.ts | 62 ---------------- .../src/avm/interpreter/interpreter.ts | 72 ------------------- 6 files changed, 99 insertions(+), 173 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/errors.ts delete mode 100644 yarn-project/acir-simulator/src/avm/interpreter/index.ts delete mode 100644 yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts delete mode 100644 yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts diff --git a/yarn-project/acir-simulator/src/avm/avm_context.test.ts b/yarn-project/acir-simulator/src/avm/avm_context.test.ts index 05e9cf10f3e..5ab69875f1f 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.test.ts @@ -1,3 +1,60 @@ -describe('Avm', () => { - it('Executes a simple call', () => {}); +import { Fr } from '@aztec/foundation/fields'; + +import { MockProxy, mock } from 'jest-mock-extended'; + +import { AvmContext } from './avm_context.js'; +import { TypeTag } from './avm_memory_types.js'; +import { initExecutionEnvironment } from './fixtures/index.js'; +import { AvmJournal } from './journal/journal.js'; +import { Add } from './opcodes/arithmetic.js'; +import { Jump, Return } from './opcodes/control_flow.js'; +import { Instruction } from './opcodes/instruction.js'; +import { CalldataCopy } from './opcodes/memory.js'; +import { InvalidProgramCounterError } from './errors.js'; + +describe('avm context', () => { + let context: AvmContext; + let journal: MockProxy; + + beforeEach(() => { + journal = mock(); + context = new AvmContext(journal) + }); + + it('Should execute a series of instructions', async () => { + const calldata: Fr[] = [new Fr(1), new Fr(2)]; + + const instructions: Instruction[] = [ + new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), + new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), + ]; + + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); + // Set instructions (skip bytecode decoding) + context.setInstructions(instructions); + const results = await context.execute() + + expect(results.reverted).toBe(false); + expect(results.revertReason).toBeUndefined(); + expect(results.output).toEqual([new Fr(3)]); + }); + + it('Should revert with an invalid jump', async () => { + const calldata: Fr[] = []; + + const invalidJumpDestination = 22; + + const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; + + context = new AvmContext(journal, initExecutionEnvironment({ calldata })); + // Set instructions (skip bytecode decoding) + context.setInstructions(instructions); + const results = await context.execute() + + expect(results.reverted).toBe(true); + expect(results.revertReason).toBeInstanceOf(InvalidProgramCounterError); + expect(results.output).toHaveLength(0); + }); }); + diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 90708ae35b7..75589433962 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -9,6 +9,7 @@ import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; import { InstructionExecutionError, type Instruction } from './opcodes/index.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; +import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeFoundInterpreterError } from './errors.js'; import { assert } from 'console'; @@ -16,8 +17,12 @@ import { assert } from 'console'; * Avm Context manages the state and execution of the AVM */ export class AvmContext { + /** Contains constant variables provided by the kernel */ + public environment: AvmExecutionEnvironment = initExecutionEnvironment(); /** VM state that is modified on an instruction-by-instruction basis */ public machineState: AvmMachineState; + /** Manages mutable state during execution - (caching, fetching) */ + public journal: AvmJournal; /** The public contract code corresponding to this context's contract class */ private instructions: Instruction[]; @@ -27,17 +32,15 @@ export class AvmContext { //private nestedExecutions: PublicExecutionResult[] = []; constructor( - /** Manages mutable state during execution - (caching, fetching) */ - public journal: AvmJournal, - /** Contains constant variables provided by the kernel */ - public environment: AvmExecutionEnvironment = initExecutionEnvironment(), + journal: AvmJournal, + environment: AvmExecutionEnvironment = initExecutionEnvironment(), /** Some initial values for machine state */ initialMachineState: InitialAvmMachineState = initInitialMachineState(), private log = createDebugLogger('aztec:avm_simulator:avm_context'), ) { + this.journal = journal; this.environment = environment; this.machineState = new AvmMachineState(initialMachineState); - this.journal = journal; // Bytecode is fetched and instructions are decoded in async init() this.instructions = []; } @@ -60,7 +63,8 @@ export class AvmContext { } /** - * For testing purposes (to skip bytecode decoding) + * Set instructions directly (then can skip bytecode decoding) + * For testing purposes only! */ setInstructions(instructions: Instruction[]) { this.instructions = instructions; @@ -98,7 +102,7 @@ export class AvmContext { return results; } catch (e) { this.log('Exceptional halt'); - if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { + if (!(e instanceof AvmExecutionError || e instanceof InstructionExecutionError)) { this.log(`Unknown error thrown by avm: ${e}`); throw e; } @@ -196,32 +200,3 @@ export class AvmContext { return nestedContext; } } - - -/** - * Avm-specific errors should derive from this - */ -export abstract class AvmInterpreterError extends Error { - constructor(message: string, ...rest: any[]) { - super(message, ...rest); - this.name = 'AvmInterpreterError'; - } -} - -class NoBytecodeFoundInterpreterError extends AvmInterpreterError { - constructor(contractAddress: AztecAddress) { - super(`No bytecode found at: ${contractAddress}`); - this.name = 'NoBytecodeFoundInterpreterError'; - } -} - -/** - * Error is thrown when the program counter goes to an invalid location. - * There is no instruction at the provided pc - */ -export class InvalidProgramCounterError extends AvmInterpreterError { - constructor(pc: number, max: number) { - super(`Invalid program counter ${pc}, max is ${max}`); - this.name = 'InvalidProgramCounterError'; - } -} diff --git a/yarn-project/acir-simulator/src/avm/errors.ts b/yarn-project/acir-simulator/src/avm/errors.ts new file mode 100644 index 00000000000..fe7cd424adf --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/errors.ts @@ -0,0 +1,29 @@ +import { AztecAddress } from "@aztec/circuits.js"; + +/** + * Avm-specific errors should derive from this + */ +export abstract class AvmExecutionError extends Error { + constructor(message: string, ...rest: any[]) { + super(message, ...rest); + this.name = 'AvmInterpreterError'; + } +} + +export class NoBytecodeFoundInterpreterError extends AvmExecutionError { + constructor(contractAddress: AztecAddress) { + super(`No bytecode found at: ${contractAddress}`); + this.name = 'NoBytecodeFoundInterpreterError'; + } +} + +/** + * Error is thrown when the program counter goes to an invalid location. + * There is no instruction at the provided pc + */ +export class InvalidProgramCounterError extends AvmExecutionError { + constructor(pc: number, max: number) { + super(`Invalid program counter ${pc}, max is ${max}`); + this.name = 'InvalidProgramCounterError'; + } +} \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/interpreter/index.ts b/yarn-project/acir-simulator/src/avm/interpreter/index.ts deleted file mode 100644 index d5189852b6e..00000000000 --- a/yarn-project/acir-simulator/src/avm/interpreter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './interpreter.js'; diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts deleted file mode 100644 index c99345211a4..00000000000 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Fr } from '@aztec/foundation/fields'; - -import { MockProxy, mock } from 'jest-mock-extended'; - -import { AvmContext, InvalidProgramCounterError } from '../avm_context.js'; -import { TypeTag } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; -import { Add } from '../opcodes/arithmetic.js'; -import { Jump, Return } from '../opcodes/control_flow.js'; -import { Instruction } from '../opcodes/instruction.js'; -import { CalldataCopy } from '../opcodes/memory.js'; - -describe('interpreter', () => { - let context: AvmContext; - let journal: MockProxy; - - beforeEach(() => { - journal = mock(); - context = new AvmContext(journal) - }); - - it('Should execute a series of instructions', async () => { - const calldata: Fr[] = [new Fr(1), new Fr(2)]; - - const instructions: Instruction[] = [ - new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), - new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), - new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), - ]; - - const contextInputs = { - environment: initExecutionEnvironment({ calldata }), - initialMachineState: initMachineState(), - }; - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - // Set instructions (skip bytecode decoding) - context.setInstructions(instructions); - const results = await context.execute() - - expect(results.reverted).toBe(false); - expect(results.revertReason).toBeUndefined(); - expect(results.output).toEqual([new Fr(3)]); - }); - - it('Should revert with an invalid jump', async () => { - const calldata: Fr[] = []; - - const invalidJumpDestination = 22; - - const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; - - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - // Set instructions (skip bytecode decoding) - context.setInstructions(instructions); - const results = await context.execute() - - expect(results.reverted).toBe(true); - expect(results.revertReason).toBeInstanceOf(InvalidProgramCounterError); - expect(results.output).toHaveLength(0); - }); -}); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts deleted file mode 100644 index 5e4cb843eb9..00000000000 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ /dev/null @@ -1,72 +0,0 @@ -//import { strict as assert } from 'assert'; -// -//import { AvmContractCallResults } from '../avm_message_call_result.js'; -//import { Instruction, InstructionExecutionError } from '../opcodes/instruction.js'; -//import { AvmContext } from '../avm_context.js'; -// -// -////export class AvmSessionExecutor { -// /** -// * Run the avm -// * @returns bool - successful execution will return true -// * - reverted execution will return false -// * - any other panic will throw -// */ -// export async function executeAvm( -// context: AvmContext, -// instructions: Instruction[] = [], -// ): Promise { -// assert(instructions.length > 0); -// // TODO unclear the separation between AvmContext and this -// -// try { -// while (!context.machineState.halted) { -// const instruction = instructions[context.machineState.pc]; -// assert(!!instruction); // This should never happen -// -// await instruction.execute(context); -// -// if (context.machineState.pc >= instructions.length) { -// throw new InvalidProgramCounterError(context.machineState.pc, /*max=*/ instructions.length); -// } -// } -// -// const returnData = context.results.output; -// if (context.results.reverted) { -// return AvmContractCallResults.revert(returnData); -// } -// -// return AvmContractCallResults.success(returnData); -// } catch (e) { -// if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { -// throw e; -// } -// -// // reverts can return data as well -// const returnData = context.results.output; -// return AvmContractCallResults.revert(returnData, /*revertReason=*/ e); -// } -// } -////} -// -///** -// * Avm-specific errors should derive from this -// */ -//export abstract class AvmInterpreterError extends Error { -// constructor(message: string, ...rest: any[]) { -// super(message, ...rest); -// this.name = 'AvmInterpreterError'; -// } -//} -// -///** -// * Error is thrown when the program counter goes to an invalid location. -// * There is no instruction at the provided pc -// */ -//export class InvalidProgramCounterError extends AvmInterpreterError { -// constructor(pc: number, max: number) { -// super(`Invalid program counter ${pc}, max is ${max}`); -// this.name = 'InvalidProgramCounterError'; -// } -//} -// \ No newline at end of file From e5256efaf1baf7e20a98068b29a3ceeb6f000703 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 19:21:38 +0000 Subject: [PATCH 04/16] world state journal refactor --- .../src/avm/avm_context.test.ts | 6 +- .../acir-simulator/src/avm/avm_context.ts | 66 +++++-------------- .../acir-simulator/src/avm/index.test.ts | 6 +- .../acir-simulator/src/avm/journal/errors.ts | 8 --- .../src/avm/journal/journal.test.ts | 54 +++++++-------- .../acir-simulator/src/avm/journal/journal.ts | 60 ++++++----------- .../src/avm/opcodes/accrued_substate.test.ts | 14 ++-- .../src/avm/opcodes/accrued_substate.ts | 8 +-- .../src/avm/opcodes/arithmetic.test.ts | 6 +- .../src/avm/opcodes/bitwise.test.ts | 6 +- .../src/avm/opcodes/comparators.test.ts | 6 +- .../src/avm/opcodes/control_flow.test.ts | 6 +- .../avm/opcodes/environment_getters.test.ts | 6 +- .../src/avm/opcodes/external_calls.test.ts | 6 +- .../src/avm/opcodes/external_calls.ts | 17 ++--- .../src/avm/opcodes/memory.test.ts | 6 +- .../src/avm/opcodes/storage.test.ts | 6 +- .../acir-simulator/src/avm/opcodes/storage.ts | 4 +- 18 files changed, 113 insertions(+), 178 deletions(-) delete mode 100644 yarn-project/acir-simulator/src/avm/journal/errors.ts diff --git a/yarn-project/acir-simulator/src/avm/avm_context.test.ts b/yarn-project/acir-simulator/src/avm/avm_context.test.ts index 5ab69875f1f..f1fc22f4454 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.test.ts @@ -5,7 +5,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from './avm_context.js'; import { TypeTag } from './avm_memory_types.js'; import { initExecutionEnvironment } from './fixtures/index.js'; -import { AvmJournal } from './journal/journal.js'; +import { AvmWorldStateJournal } from './journal/journal.js'; import { Add } from './opcodes/arithmetic.js'; import { Jump, Return } from './opcodes/control_flow.js'; import { Instruction } from './opcodes/instruction.js'; @@ -14,10 +14,10 @@ import { InvalidProgramCounterError } from './errors.js'; describe('avm context', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(() => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 75589433962..7a1ec62e6e2 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -4,7 +4,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; -import { AvmJournal } from './journal/journal.js'; +import { AvmWorldStateJournal } from './journal/journal.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; import { InstructionExecutionError, type Instruction } from './opcodes/index.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; @@ -22,7 +22,7 @@ export class AvmContext { /** VM state that is modified on an instruction-by-instruction basis */ public machineState: AvmMachineState; /** Manages mutable state during execution - (caching, fetching) */ - public journal: AvmJournal; + public worldState: AvmWorldStateJournal; /** The public contract code corresponding to this context's contract class */ private instructions: Instruction[]; @@ -32,13 +32,13 @@ export class AvmContext { //private nestedExecutions: PublicExecutionResult[] = []; constructor( - journal: AvmJournal, + worldState: AvmWorldStateJournal, environment: AvmExecutionEnvironment = initExecutionEnvironment(), /** Some initial values for machine state */ initialMachineState: InitialAvmMachineState = initInitialMachineState(), private log = createDebugLogger('aztec:avm_simulator:avm_context'), ) { - this.journal = journal; + this.worldState = worldState; this.environment = environment; this.machineState = new AvmMachineState(initialMachineState); // Bytecode is fetched and instructions are decoded in async init() @@ -48,7 +48,7 @@ export class AvmContext { async init() { // NOTE: the following is mocked as getPublicBytecode does not exist yet const selector = new FunctionSelector(0); - const bytecode = await this.journal.hostStorage.contractsDb.getBytecode( + const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode( this.environment.address, selector, ); @@ -114,51 +114,15 @@ export class AvmContext { } } - /** - * Merge the journal of this call with it's parent - * NOTE: this should never be called on a root context - only from within a nested call - */ - mergeJournalSuccess() { - this.journal.mergeSuccessWithParent(); - } - - /** - * Merge the journal of this call with it's parent - * For when the child call fails ( we still must track state accesses ) - */ - mergeJournalFailure() { - this.journal.mergeFailureWithParent(); - } - - /** - * Create a new forked avm context - for internal calls - */ - //public newWithForkedState(): AvmContext { - // const forkedState = AvmJournal.branchParent(this.journal); - // return new AvmContext(this.environment, forkedState); - //} - - /** - * Create a new forked avm context - for external calls - */ - public static newWithForkedState( - environment: AvmExecutionEnvironment, - initialMachineState: InitialAvmMachineState, - journal: AvmJournal - ): AvmContext { - const forkedState = AvmJournal.branchParent(journal); - return new AvmContext(forkedState); - } - /** * Prepare a new AVM context that will be ready for an external call - * - It will fork the journal - * - It will set the correct execution Environment Variables for a call + * - Fork the world state journal + * - Set the correct execution environment variables for a call * - Alter both address and storageAddress * * @param address - The contract to call * @param parentEnvironment - The current execution environment - * @param journal - The current journal + * @param parentWorldState - The current world state (journal) * @returns new AvmContext instance */ public static async createNestedContractCallContext( @@ -166,10 +130,10 @@ export class AvmContext { calldata: Fr[], parentEnvironment: AvmExecutionEnvironment, initialMachineState: InitialAvmMachineState, - journal: AvmJournal, + parentWorldState: AvmWorldStateJournal, ): Promise { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedCall(address, calldata); - const forkedState = AvmJournal.branchParent(journal); + const forkedState = parentWorldState.fork(); const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); await nestedContext.init(); return nestedContext; @@ -177,13 +141,13 @@ export class AvmContext { /** * Prepare a new AVM context that will be ready for an external static call - * - It will fork the journal - * - It will set the correct execution Environment Variables for a call + * - Fork the world state journal + * - Set the correct execution environment variables for a call * - Alter both address and storageAddress * * @param address - The contract to call * @param parentEnvironment - The current execution environment - * @param journal - The current journal + * @param parentWorldState - The current world state (journal) * @returns new AvmContext instance */ public static async createNestedStaticCallContext( @@ -191,10 +155,10 @@ export class AvmContext { calldata: Fr[], parentEnvironment: AvmExecutionEnvironment, initialMachineState: InitialAvmMachineState, - journal: AvmJournal, + parentWorldState: AvmWorldStateJournal, ): Promise { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedStaticCall(address, calldata); - const forkedState = AvmJournal.branchParent(journal); + const forkedState = parentWorldState.fork(); const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); await nestedContext.init(); return nestedContext; diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 03457e01e43..e21ef17e3d4 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -8,13 +8,13 @@ import { TypeTag } from './avm_memory_types.js'; import { AvmContext } from './avm_context.js'; import { initExecutionEnvironment } from './fixtures/index.js'; import { HostStorage } from './journal/host_storage.js'; -import { AvmJournal } from './journal/journal.js'; +import { AvmWorldStateJournal } from './journal/journal.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; describe('avm', () => { - let journal: AvmJournal; + let journal: AvmWorldStateJournal; let contractsDb: MockProxy; beforeEach(() => { @@ -23,7 +23,7 @@ describe('avm', () => { const commitmentsDb = mock(); const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); - journal = new AvmJournal(hostStorage); + journal = new AvmWorldStateJournal(hostStorage); }); it('Should execute bytecode that performs basic addition', async () => { diff --git a/yarn-project/acir-simulator/src/avm/journal/errors.ts b/yarn-project/acir-simulator/src/avm/journal/errors.ts deleted file mode 100644 index 17696b1de68..00000000000 --- a/yarn-project/acir-simulator/src/avm/journal/errors.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Error thrown when a base journal is attempted to be merged. - */ -export class RootJournalCannotBeMerged extends Error { - constructor() { - super('Root journal cannot be merged'); - } -} diff --git a/yarn-project/acir-simulator/src/avm/journal/journal.test.ts b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts index 2a3aafd6da6..0a2b8c4b1b3 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.test.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts @@ -3,13 +3,12 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; -import { RootJournalCannotBeMerged } from './errors.js'; import { HostStorage } from './host_storage.js'; -import { AvmJournal, JournalData } from './journal.js'; +import { AvmWorldStateJournal, JournalData } from './journal.js'; describe('journal', () => { let publicDb: MockProxy; - let journal: AvmJournal; + let journal: AvmWorldStateJournal; beforeEach(() => { publicDb = mock(); @@ -17,7 +16,7 @@ describe('journal', () => { const contractsDb = mock(); const hostStorage = new HostStorage(publicDb, contractsDb, commitmentsDb); - journal = new AvmJournal(hostStorage); + journal = new AvmWorldStateJournal(hostStorage); }); describe('Public Storage', () => { @@ -43,7 +42,7 @@ describe('journal', () => { publicDb.storageRead.mockResolvedValue(Promise.resolve(storedValue)); - const childJournal = new AvmJournal(journal.hostStorage, journal); + const childJournal = new AvmWorldStateJournal(journal.hostStorage, journal); // Get the cache miss const cacheMissResult = await childJournal.readStorage(contractAddress, key); @@ -144,15 +143,15 @@ describe('journal', () => { journal.writeL1Message(logs); journal.writeNullifier(commitment); - const journal1 = new AvmJournal(journal.hostStorage, journal); - journal1.writeStorage(contractAddress, key, valueT1); - await journal1.readStorage(contractAddress, key); - journal1.writeNoteHash(commitmentT1); - journal1.writeLog(logsT1); - journal1.writeL1Message(logsT1); - journal1.writeNullifier(commitmentT1); + const childJournal = new AvmWorldStateJournal(journal.hostStorage, journal); + childJournal.writeStorage(contractAddress, key, valueT1); + await childJournal.readStorage(contractAddress, key); + childJournal.writeNoteHash(commitmentT1); + childJournal.writeLog(logsT1); + childJournal.writeL1Message(logsT1); + childJournal.writeNullifier(commitmentT1); - journal1.mergeSuccessWithParent(); + journal.acceptNestedWorldState(childJournal); const result = await journal.readStorage(contractAddress, key); expect(result).toEqual(valueT1); @@ -204,15 +203,15 @@ describe('journal', () => { journal.writeL1Message(logs); journal.writeNullifier(commitment); - const journal1 = new AvmJournal(journal.hostStorage, journal); - journal1.writeStorage(contractAddress, key, valueT1); - await journal1.readStorage(contractAddress, key); - journal1.writeNoteHash(commitmentT1); - journal1.writeLog(logsT1); - journal1.writeL1Message(logsT1); - journal1.writeNullifier(commitmentT1); + const childJournal = new AvmWorldStateJournal(journal.hostStorage, journal); + childJournal.writeStorage(contractAddress, key, valueT1); + await childJournal.readStorage(contractAddress, key); + childJournal.writeNoteHash(commitmentT1); + childJournal.writeLog(logsT1); + childJournal.writeL1Message(logsT1); + childJournal.writeNullifier(commitmentT1); - journal1.mergeFailureWithParent(); + journal.rejectNestedWorldState(childJournal); // Check that the storage is reverted by reading from the journal const result = await journal.readStorage(contractAddress, key); @@ -239,14 +238,11 @@ describe('journal', () => { expect(journalUpdates.newNullifiers).toEqual([commitment]); }); - it('Cannot merge a root journal, but can merge a child journal', () => { - const rootJournal = AvmJournal.rootJournal(journal.hostStorage); - const childJournal = AvmJournal.branchParent(rootJournal); + it('Can fork and merge journals', () => { + const rootJournal = new AvmWorldStateJournal(journal.hostStorage); + const childJournal = rootJournal.fork(); - expect(() => rootJournal.mergeSuccessWithParent()).toThrow(RootJournalCannotBeMerged); - expect(() => rootJournal.mergeFailureWithParent()).toThrow(RootJournalCannotBeMerged); - - expect(() => childJournal.mergeSuccessWithParent()); - expect(() => childJournal.mergeFailureWithParent()); + expect(() => rootJournal.acceptNestedWorldState(childJournal)); + expect(() => rootJournal.rejectNestedWorldState(childJournal)); }); }); diff --git a/yarn-project/acir-simulator/src/avm/journal/journal.ts b/yarn-project/acir-simulator/src/avm/journal/journal.ts index 3b2445e7dc6..cdeb4b4ff92 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.ts @@ -1,6 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; -import { RootJournalCannotBeMerged } from './errors.js'; import { HostStorage } from './host_storage.js'; /** @@ -25,11 +24,11 @@ export type JournalData = { * A cache of the current state of the AVM * The interpreter should make any state queries through this object * - * When a sub context's call succeeds, it's journal is merge into the parent - * When a a call fails, it's journal is discarded and the parent is used from this point forward + * When a nested context succeeds, it's journal is merge into the parent + * When a call fails, it's journal is discarded and the parent is used from this point forward * When a call succeeds's we can merge a child into its parent */ -export class AvmJournal { +export class AvmWorldStateJournal { /** Reference to node storage */ public readonly hostStorage: HostStorage; @@ -49,27 +48,18 @@ export class AvmJournal { // contract address -> key -> value private currentStorageValue: Map> = new Map(); - private parentJournal: AvmJournal | undefined; + private parentJournal: AvmWorldStateJournal | undefined; - constructor(hostStorage: HostStorage, parentJournal?: AvmJournal) { + constructor(hostStorage: HostStorage, parentJournal?: AvmWorldStateJournal) { this.hostStorage = hostStorage; this.parentJournal = parentJournal; } /** - * Create a new root journal, without a parent - * @param hostStorage - + * Create a new world state journal forked from this one */ - public static rootJournal(hostStorage: HostStorage) { - return new AvmJournal(hostStorage); - } - - /** - * Create a new journal from a parent - * @param parentJournal - - */ - public static branchParent(parentJournal: AvmJournal) { - return new AvmJournal(parentJournal.hostStorage, parentJournal); + public fork() { + return new AvmWorldStateJournal(this.hostStorage, this); } /** @@ -162,44 +152,36 @@ export class AvmJournal { } /** - * Merge Journal from successful call into parent + * Accept nested world state, merging in its journal, and accepting its state modifications * - Utxo objects are concatenated * - Public state changes are merged, with the value in the incoming journal taking precedent * - Public state journals (r/w logs), with the accessing being appended in chronological order */ - public mergeSuccessWithParent() { - if (!this.parentJournal) { - throw new RootJournalCannotBeMerged(); - } - + public acceptNestedWorldState(nestedJournal: AvmWorldStateJournal) { // Merge UTXOs - this.parentJournal.newNoteHashes = this.parentJournal.newNoteHashes.concat(this.newNoteHashes); - this.parentJournal.newL1Messages = this.parentJournal.newL1Messages.concat(this.newL1Messages); - this.parentJournal.newNullifiers = this.parentJournal.newNullifiers.concat(this.newNullifiers); - this.parentJournal.newLogs = this.parentJournal.newLogs.concat(this.newLogs); + this.newNoteHashes = this.newNoteHashes.concat(nestedJournal.newNoteHashes); + this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages); + this.newNullifiers = this.newNullifiers.concat(nestedJournal.newNullifiers); + this.newLogs = this.newLogs.concat(nestedJournal.newLogs); // Merge Public State - mergeCurrentValueMaps(this.parentJournal.currentStorageValue, this.currentStorageValue); + mergeCurrentValueMaps(this.currentStorageValue, nestedJournal.currentStorageValue); // Merge storage read and write journals - mergeContractJournalMaps(this.parentJournal.storageReads, this.storageReads); - mergeContractJournalMaps(this.parentJournal.storageWrites, this.storageWrites); + mergeContractJournalMaps(this.storageReads, nestedJournal.storageReads); + mergeContractJournalMaps(this.storageWrites, nestedJournal.storageWrites); } /** - * Merge Journal for failed call into parent + * Reject nested world state, merging in its journal, but not accepting its state modifications * - Utxo objects are concatenated * - Public state changes are dropped * - Public state journals (r/w logs) are maintained, with the accessing being appended in chronological order */ - public mergeFailureWithParent() { - if (!this.parentJournal) { - throw new RootJournalCannotBeMerged(); - } - + public rejectNestedWorldState(nestedJournal: AvmWorldStateJournal) { // Merge storage read and write journals - mergeContractJournalMaps(this.parentJournal.storageReads, this.storageReads); - mergeContractJournalMaps(this.parentJournal.storageWrites, this.storageWrites); + mergeContractJournalMaps(this.storageReads, nestedJournal.storageReads); + mergeContractJournalMaps(this.storageWrites, nestedJournal.storageWrites); } /** diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index a0e93deac40..cd82e062be5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -4,17 +4,17 @@ import { Field } from '../avm_memory_types.js'; import { AvmContext } from '../avm_context.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; import { StaticCallStorageAlterError } from './storage.js'; describe('Accrued Substate', () => { let context: AvmContext; - let journal: AvmJournal; + let journal: AvmWorldStateJournal; beforeEach(() => { const hostStorage = mock(); - journal = new AvmJournal(hostStorage); + journal = new AvmWorldStateJournal(hostStorage); context = new AvmContext(journal) }); @@ -37,7 +37,7 @@ describe('Accrued Substate', () => { await new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0).execute(context); - const journalState = context.journal.flush(); + const journalState = context.worldState.flush(); const expected = [value.toFr()]; expect(journalState.newNoteHashes).toEqual(expected); }); @@ -62,7 +62,7 @@ describe('Accrued Substate', () => { await new EmitNullifier(/*indirect=*/ 0, /*offset=*/ 0).execute(context); - const journalState = context.journal.flush(); + const journalState = context.worldState.flush(); const expected = [value.toFr()]; expect(journalState.newNullifiers).toEqual(expected); }); @@ -92,7 +92,7 @@ describe('Accrued Substate', () => { await new EmitUnencryptedLog(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(context); - const journalState = context.journal.flush(); + const journalState = context.worldState.flush(); const expected = values.map(v => v.toFr()); expect(journalState.newLogs).toEqual([expected]); }); @@ -122,7 +122,7 @@ describe('Accrued Substate', () => { await new SendL2ToL1Message(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(context); - const journalState = context.journal.flush(); + const journalState = context.worldState.flush(); const expected = values.map(v => v.toFr()); expect(journalState.newL1Messages).toEqual([expected]); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index b6a762ceac7..a59da301337 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -19,7 +19,7 @@ export class EmitNoteHash extends Instruction { } const noteHash = context.machineState.memory.get(this.noteHashOffset).toFr(); - context.journal.writeNoteHash(noteHash); + context.worldState.writeNoteHash(noteHash); context.machineState.incrementPc(); } @@ -41,7 +41,7 @@ export class EmitNullifier extends Instruction { } const nullifier = context.machineState.memory.get(this.nullifierOffset).toFr(); - context.journal.writeNullifier(nullifier); + context.worldState.writeNullifier(nullifier); context.machineState.incrementPc(); } @@ -63,7 +63,7 @@ export class EmitUnencryptedLog extends Instruction { } const log = context.machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr()); - context.journal.writeLog(log); + context.worldState.writeLog(log); context.machineState.incrementPc(); } @@ -85,7 +85,7 @@ export class SendL2ToL1Message extends Instruction { } const msg = context.machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr()); - context.journal.writeL1Message(msg); + context.worldState.writeL1Message(msg); context.machineState.incrementPc(); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 3364124f08a..3da97cdf93e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -3,15 +3,15 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(() => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 14edc1ac1ba..810ff4aef64 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -3,15 +3,15 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; describe('Bitwise instructions', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(async () => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index a800e199664..effcc2cd8ea 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -3,16 +3,16 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { Eq, Lt, Lte } from './comparators.js'; import { InstructionExecutionError } from './instruction.js'; describe('Comparators', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(async () => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index bacaaf78002..0bb4f253d37 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -4,16 +4,16 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; import { InstructionExecutionError } from './instruction.js'; describe('Control Flow Opcodes', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(() => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 27d083146e8..7eff8f481d2 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -4,7 +4,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { initExecutionEnvironment, initGlobalVariables, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { Address, BlockNumber, @@ -22,10 +22,10 @@ import { describe('Environment getters instructions', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(() => { - journal = mock(); + journal = mock(); }); type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index ca1fd9b90da..f1b7055488a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -8,7 +8,7 @@ import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; import { Return } from './control_flow.js'; import { Call, StaticCall } from './external_calls.js'; @@ -18,7 +18,7 @@ import { SStore } from './storage.js'; describe('External Calls', () => { let context: AvmContext; - let journal: AvmJournal; + let journal: AvmWorldStateJournal; let contractsDb: MockProxy; @@ -28,7 +28,7 @@ describe('External Calls', () => { const commitmentsDb = mock(); const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); - journal = new AvmJournal(hostStorage); + journal = new AvmWorldStateJournal(hostStorage); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 5a202f67256..a9871d81ba9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -4,6 +4,7 @@ import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +import { initMachineState } from '../fixtures/index.js'; export class Call extends Instruction { @@ -44,8 +45,8 @@ export class Call extends Instruction { new Fr(callAddress.toBigInt()), calldata, context.environment, - { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0}, - context.journal, + initMachineState(), + context.worldState, ); const nestedCallResults = await nestedContext.execute(); @@ -60,9 +61,9 @@ export class Call extends Instruction { context.machineState.memory.setSlice(this.retOffset, convertedReturnData); if (success) { - nestedContext.mergeJournalSuccess(); + context.worldState.acceptNestedWorldState(nestedContext.worldState); } else { - nestedContext.mergeJournalFailure(); + context.worldState.rejectNestedWorldState(nestedContext.worldState); } context.machineState.incrementPc(); @@ -106,8 +107,8 @@ export class StaticCall extends Instruction { new Fr(callAddress.toBigInt()), calldata, context.environment, - { l1GasLeft: 0, l2GasLeft: 0, daGasLeft: 0}, - context.journal, + initMachineState(), + context.worldState, ); const nestedCallResults = await nestedContext.execute(); @@ -122,9 +123,9 @@ export class StaticCall extends Instruction { context.machineState.memory.setSlice(this.retOffset, convertedReturnData); if (success) { - nestedContext.mergeJournalSuccess(); + context.worldState.acceptNestedWorldState(nestedContext.worldState); } else { - nestedContext.mergeJournalFailure(); + context.worldState.rejectNestedWorldState(nestedContext.worldState); } context.machineState.incrementPc(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 4a095718a16..3fe13538bf8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -5,17 +5,17 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; import { init } from '@aztec/foundation/crypto'; describe('Memory instructions', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; beforeEach(async () => { - journal = mock(); + journal = mock(); context = new AvmContext(journal) }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 3651d1582c8..1a8d6efb31b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -6,16 +6,16 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; -import { AvmJournal } from '../journal/journal.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; import { SLoad, SStore, StaticCallStorageAlterError } from './storage.js'; describe('Storage Instructions', () => { let context: AvmContext; - let journal: MockProxy; + let journal: MockProxy; const address = AztecAddress.random(); beforeEach(async () => { - journal = mock(); + journal = mock(); context = new AvmContext(journal, initExecutionEnvironment({ address, storageAddress: address })); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index 5ac12ef5d06..a5b4428d7f8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -35,7 +35,7 @@ export class SStore extends BaseStorageInstruction { const slot = context.machineState.memory.get(this.aOffset); const data = context.machineState.memory.get(this.bOffset); - context.journal.writeStorage( + context.worldState.writeStorage( context.environment.storageAddress, new Fr(slot.toBigInt()), new Fr(data.toBigInt()), @@ -56,7 +56,7 @@ export class SLoad extends BaseStorageInstruction { async execute(context: AvmContext): Promise { const slot = context.machineState.memory.get(this.aOffset); - const data: Fr = await context.journal.readStorage( + const data: Fr = await context.worldState.readStorage( context.environment.storageAddress, new Fr(slot.toBigInt()), ); From 7d9b10db8c6c6595c0b40814a0388938e10686db Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 20:51:28 +0000 Subject: [PATCH 05/16] formatting fix --- .../src/avm/avm_context.test.ts | 9 ++++---- .../acir-simulator/src/avm/avm_context.ts | 17 ++++++-------- .../src/avm/avm_machine_state.ts | 11 ++++----- yarn-project/acir-simulator/src/avm/errors.ts | 4 ++-- .../acir-simulator/src/avm/fixtures/index.ts | 2 +- .../acir-simulator/src/avm/index.test.ts | 12 ++++------ .../src/avm/opcodes/accrued_substate.test.ts | 6 ++--- .../src/avm/opcodes/arithmetic.test.ts | 3 +-- .../src/avm/opcodes/bitwise.test.ts | 23 +++++++++---------- .../src/avm/opcodes/comparators.test.ts | 3 +-- .../src/avm/opcodes/control_flow.test.ts | 2 +- .../src/avm/opcodes/control_flow.ts | 4 +--- .../avm/opcodes/environment_getters.test.ts | 2 +- .../src/avm/opcodes/external_calls.test.ts | 3 +-- .../src/avm/opcodes/external_calls.ts | 13 +++++++---- .../src/avm/opcodes/instruction.ts | 5 ++-- .../src/avm/opcodes/memory.test.ts | 21 +++++++++++------ .../src/avm/opcodes/storage.test.ts | 5 ++-- .../acir-simulator/src/avm/opcodes/storage.ts | 5 +--- 19 files changed, 70 insertions(+), 80 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.test.ts b/yarn-project/acir-simulator/src/avm/avm_context.test.ts index f1fc22f4454..a4c270dfb2e 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.test.ts @@ -4,13 +4,13 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from './avm_context.js'; import { TypeTag } from './avm_memory_types.js'; +import { InvalidProgramCounterError } from './errors.js'; import { initExecutionEnvironment } from './fixtures/index.js'; import { AvmWorldStateJournal } from './journal/journal.js'; import { Add } from './opcodes/arithmetic.js'; import { Jump, Return } from './opcodes/control_flow.js'; import { Instruction } from './opcodes/instruction.js'; import { CalldataCopy } from './opcodes/memory.js'; -import { InvalidProgramCounterError } from './errors.js'; describe('avm context', () => { let context: AvmContext; @@ -18,7 +18,7 @@ describe('avm context', () => { beforeEach(() => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); it('Should execute a series of instructions', async () => { @@ -33,7 +33,7 @@ describe('avm context', () => { context = new AvmContext(journal, initExecutionEnvironment({ calldata })); // Set instructions (skip bytecode decoding) context.setInstructions(instructions); - const results = await context.execute() + const results = await context.execute(); expect(results.reverted).toBe(false); expect(results.revertReason).toBeUndefined(); @@ -50,11 +50,10 @@ describe('avm context', () => { context = new AvmContext(journal, initExecutionEnvironment({ calldata })); // Set instructions (skip bytecode decoding) context.setInstructions(instructions); - const results = await context.execute() + const results = await context.execute(); expect(results.reverted).toBe(true); expect(results.revertReason).toBeInstanceOf(InvalidProgramCounterError); expect(results.output).toHaveLength(0); }); }); - diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 7a1ec62e6e2..32b3075ef73 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -2,16 +2,16 @@ import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import { assert } from 'console'; + import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; -import { AvmWorldStateJournal } from './journal/journal.js'; -import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; -import { InstructionExecutionError, type Instruction } from './opcodes/index.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; -import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeFoundInterpreterError } from './errors.js'; - -import { assert } from 'console'; +import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; +import { AvmWorldStateJournal } from './journal/journal.js'; +import { type Instruction, InstructionExecutionError } from './opcodes/index.js'; +import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; /** * Avm Context manages the state and execution of the AVM @@ -48,10 +48,7 @@ export class AvmContext { async init() { // NOTE: the following is mocked as getPublicBytecode does not exist yet const selector = new FunctionSelector(0); - const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode( - this.environment.address, - selector, - ); + const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode(this.environment.address, selector); // This assumes that we will not be able to send messages to accounts without code // Pending classes and instances impl details diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 3244e41e7c2..e126bbbb656 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -1,15 +1,15 @@ //import { Fr } from '@aztec/foundation/fields'; - //import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { Fr } from '@aztec/circuits.js'; + import { TaggedMemory } from './avm_memory_types.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; export type InitialAvmMachineState = { - l1GasLeft: number, - l2GasLeft: number, - daGasLeft: number, -} + l1GasLeft: number; + l2GasLeft: number; + daGasLeft: number; +}; /** * Store's data for an Avm execution frame @@ -21,7 +21,6 @@ export class AvmMachineState { // */ //public readonly executionEnvironment: AvmExecutionEnvironment; - public l1GasLeft: number; public l2GasLeft: number; public daGasLeft: number; diff --git a/yarn-project/acir-simulator/src/avm/errors.ts b/yarn-project/acir-simulator/src/avm/errors.ts index fe7cd424adf..c97258e74d8 100644 --- a/yarn-project/acir-simulator/src/avm/errors.ts +++ b/yarn-project/acir-simulator/src/avm/errors.ts @@ -1,4 +1,4 @@ -import { AztecAddress } from "@aztec/circuits.js"; +import { AztecAddress } from '@aztec/circuits.js'; /** * Avm-specific errors should derive from this @@ -26,4 +26,4 @@ export class InvalidProgramCounterError extends AvmExecutionError { super(`Invalid program counter ${pc}, max is ${max}`); this.name = 'InvalidProgramCounterError'; } -} \ No newline at end of file +} diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index e8184b11987..689f17241b3 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -60,4 +60,4 @@ export function initMachineState(overrides?: Partial): AvmMachi l2GasLeft: overrides?.l2GasLeft ?? 0, daGasLeft: overrides?.daGasLeft ?? 0, }); -} \ No newline at end of file +} diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index e21ef17e3d4..6285c8e0eb7 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -4,14 +4,14 @@ import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; -import { TypeTag } from './avm_memory_types.js'; +import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; import { AvmContext } from './avm_context.js'; +import { TypeTag } from './avm_memory_types.js'; import { initExecutionEnvironment } from './fixtures/index.js'; import { HostStorage } from './journal/host_storage.js'; import { AvmWorldStateJournal } from './journal/journal.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; -import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; describe('avm', () => { let journal: AvmWorldStateJournal; @@ -36,9 +36,7 @@ describe('avm', () => { new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), ]); - jest - .spyOn(journal.hostStorage.contractsDb, 'getBytecode') - .mockReturnValue(Promise.resolve(bytecode)); + jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); @@ -65,9 +63,7 @@ describe('avm', () => { // Decode bytecode into instructions const bytecode = Buffer.from(addArtifact.bytecode, 'base64'); - jest - .spyOn(journal.hostStorage.contractsDb, 'getBytecode') - .mockReturnValue(Promise.resolve(bytecode)); + jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index cd82e062be5..6de98453044 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -1,8 +1,8 @@ import { mock } from 'jest-mock-extended'; -import { Field } from '../avm_memory_types.js'; import { AvmContext } from '../avm_context.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; @@ -15,7 +15,7 @@ describe('Accrued Substate', () => { beforeEach(() => { const hostStorage = mock(); journal = new AvmWorldStateJournal(hostStorage); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('EmitNoteHash', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 3da97cdf93e..e5425647ff9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -2,7 +2,6 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; @@ -12,7 +11,7 @@ describe('Arithmetic Instructions', () => { beforeEach(() => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('Add', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 810ff4aef64..59291479683 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -2,7 +2,6 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; @@ -12,7 +11,7 @@ describe('Bitwise instructions', () => { beforeEach(async () => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('AND', () => { @@ -47,7 +46,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const actual = context.machineState.memory.get(2); expect(actual).toEqual(new Uint32(0b11100100010001000100n)); @@ -89,7 +88,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint32(0b11111110111011101111n); const actual = context.machineState.memory.get(2); @@ -132,7 +131,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint32(0b00011010101010101011n); const actual = context.machineState.memory.get(2); @@ -175,7 +174,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = a; const actual = context.machineState.memory.get(2); @@ -195,7 +194,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint32(0b00111111100100111001n); const actual = context.machineState.memory.get(2); @@ -215,7 +214,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint32(0b01n); const actual = context.machineState.memory.get(2); @@ -258,7 +257,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = a; const actual = context.machineState.memory.get(2); @@ -278,7 +277,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint32(0b1111111001001110010000n); const actual = context.machineState.memory.get(2); @@ -298,7 +297,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint16(0n); const actual = context.machineState.memory.get(2); @@ -318,7 +317,7 @@ describe('Bitwise instructions', () => { /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2, - ).execute(context) + ).execute(context); const expected = new Uint16(0b1001001110011100n); const actual = context.machineState.memory.get(2); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index effcc2cd8ea..8eb016d00eb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -2,7 +2,6 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Eq, Lt, Lte } from './comparators.js'; import { InstructionExecutionError } from './instruction.js'; @@ -13,7 +12,7 @@ describe('Comparators', () => { beforeEach(async () => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('Eq', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 0bb4f253d37..8fdb6759265 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -14,7 +14,7 @@ describe('Control Flow Opcodes', () => { beforeEach(() => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('JUMP', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 3afdc7281a9..2fdfced13ef 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -19,9 +19,7 @@ export class Return extends Instruction { } async execute(context: AvmContext): Promise { - const output = context.machineState.memory - .getSlice(this.returnOffset, this.copySize) - .map(word => word.toFr()); + const output = context.machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); context.machineState.return(output); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 7eff8f481d2..fc9ebbdc5aa 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; -import { initExecutionEnvironment, initGlobalVariables, initMachineState } from '../fixtures/index.js'; +import { initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Address, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index f1b7055488a..d7285fcbe17 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -6,7 +6,6 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; @@ -29,7 +28,7 @@ describe('External Calls', () => { const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); journal = new AvmWorldStateJournal(hostStorage); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('Call', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index a9871d81ba9..6baa09fd545 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -2,10 +2,9 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; +import { initMachineState } from '../fixtures/index.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; -import { initMachineState } from '../fixtures/index.js'; - export class Call extends Instruction { static type: string = 'CALL'; @@ -39,7 +38,9 @@ export class Call extends Instruction { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment async execute(context: AvmContext): Promise { const callAddress = context.machineState.memory.getAs(this.addrOffset); - const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); + const calldata = context.machineState.memory + .getSlice(this.argsOffset, this.argsSize) + .map(f => new Fr(f.toBigInt())); const nestedContext = await AvmContext.createNestedContractCallContext( new Fr(callAddress.toBigInt()), @@ -101,7 +102,9 @@ export class StaticCall extends Instruction { async execute(context: AvmContext): Promise { const callAddress = context.machineState.memory.get(this.addrOffset); - const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); + const calldata = context.machineState.memory + .getSlice(this.argsOffset, this.argsSize) + .map(f => new Fr(f.toBigInt())); const nestedContext = await AvmContext.createNestedStaticCallContext( new Fr(callAddress.toBigInt()), @@ -130,4 +133,4 @@ export class StaticCall extends Instruction { context.machineState.incrementPc(); } -} \ No newline at end of file +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 4a6c3b9cd5b..1a57ca5eab8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,10 +1,10 @@ import { assert } from 'console'; +import type { AvmContext } from '../avm_context.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; -import type { AvmContext } from '../avm_context.js'; /** * Parent class for all AVM instructions. @@ -14,13 +14,12 @@ export abstract class Instruction { static readonly type: string | undefined; public static getName(): string { - return this.type ? this.type: 'INVALID INSTRUCTION'; + return this.type ? this.type : 'INVALID INSTRUCTION'; } public toString(): string { let instructionStr = this.constructor.name + ': '; for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { instructionStr += `${prop}:${this[prop].toString()}, `; - } return instructionStr; } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 3fe13538bf8..1709e99ffe2 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -4,11 +4,10 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; -import { initExecutionEnvironment, initMachineState } from '../fixtures/index.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; -import { init } from '@aztec/foundation/crypto'; describe('Memory instructions', () => { let context: AvmContext; @@ -16,7 +15,7 @@ describe('Memory instructions', () => { beforeEach(async () => { journal = mock(); - context = new AvmContext(journal) + context = new AvmContext(journal); }); describe('SET', () => { @@ -283,7 +282,9 @@ describe('Memory instructions', () => { context.machineState.memory.set(1, new Uint16(456)); // B context.machineState.memory.set(2, new Uint8(2)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + context, + ); const actual = context.machineState.memory.get(3); const tag = context.machineState.memory.getTag(3); @@ -296,7 +297,9 @@ describe('Memory instructions', () => { context.machineState.memory.set(1, new Uint16(456)); // B context.machineState.memory.set(2, new Uint8(0)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + context, + ); const actual = context.machineState.memory.get(3); const tag = context.machineState.memory.getTag(3); @@ -309,7 +312,9 @@ describe('Memory instructions', () => { context.machineState.memory.set(1, new Uint16(456)); // B context.machineState.memory.set(2, new Field(1)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + context, + ); const actual = context.machineState.memory.get(3); const tag = context.machineState.memory.getTag(3); @@ -322,7 +327,9 @@ describe('Memory instructions', () => { context.machineState.memory.set(1, new Uint16(456)); // B context.machineState.memory.set(2, new Field(0)); // Condition - await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(context); + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + context, + ); const actual = context.machineState.memory.get(3); const tag = context.machineState.memory.getTag(3); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 1a8d6efb31b..830cf5486c0 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -46,7 +46,7 @@ describe('Storage Instructions', () => { }); it('Should not be able to write to storage in a static call', async () => { - context = new AvmContext(journal, initExecutionEnvironment( { isStaticCall: true })); + context = new AvmContext(journal, initExecutionEnvironment({ isStaticCall: true })); const a = new Field(1n); const b = new Field(2n); @@ -54,8 +54,7 @@ describe('Storage Instructions', () => { context.machineState.memory.set(0, a); context.machineState.memory.set(1, b); - const instruction = () => - new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); + const instruction = () => new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(context); await expect(instruction()).rejects.toThrow(StaticCallStorageAlterError); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index a5b4428d7f8..35af2206ae5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -56,10 +56,7 @@ export class SLoad extends BaseStorageInstruction { async execute(context: AvmContext): Promise { const slot = context.machineState.memory.get(this.aOffset); - const data: Fr = await context.worldState.readStorage( - context.environment.storageAddress, - new Fr(slot.toBigInt()), - ); + const data: Fr = await context.worldState.readStorage(context.environment.storageAddress, new Fr(slot.toBigInt())); context.machineState.memory.set(this.bOffset, new Field(data)); From f5417da64bdac82aee88c066050f1325c8a90886 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 21:00:02 +0000 Subject: [PATCH 06/16] cleanup --- .../src/avm/opcodes/instruction.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 1a57ca5eab8..10c56297f96 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -11,18 +11,6 @@ import { OperandType, deserialize, serialize } from '../serialization/instructio * It's most important aspects are execution and (de)serialization. */ export abstract class Instruction { - static readonly type: string | undefined; - - public static getName(): string { - return this.type ? this.type : 'INVALID INSTRUCTION'; - } - public toString(): string { - let instructionStr = this.constructor.name + ': '; - for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { - instructionStr += `${prop}:${this[prop].toString()}, `; - } - return instructionStr; - } public abstract execute(context: AvmContext): Promise; static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { @@ -57,6 +45,20 @@ export abstract class Instruction { assert(this instanceof Instruction); return serialize(this.constructor.wireFormat, this); } + + /** + * Generate a string representation of the instruction including + * the instruction sub-class name all of its flags and operands. + * @returns Thee string representation. + */ + public toString(): string { + let instructionStr = this.constructor.name + ': '; + // assumes that all properties are flags or operands + for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { + instructionStr += `${prop}:${this[prop].toString()}, `; + } + return instructionStr; + } } /** From cadf79195fb173bb1a4b96097e21a1b6342b6a7d Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 21:08:30 +0000 Subject: [PATCH 07/16] cleanup machine state --- .../src/avm/avm_machine_state.ts | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index e126bbbb656..fba8854bf6e 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -1,5 +1,3 @@ -//import { Fr } from '@aztec/foundation/fields'; -//import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { Fr } from '@aztec/circuits.js'; import { TaggedMemory } from './avm_memory_types.js'; @@ -12,45 +10,35 @@ export type InitialAvmMachineState = { }; /** - * Store's data for an Avm execution frame + * Avm state modified on an instruction-per-instruction basis. */ export class AvmMachineState { - ///** - // * Execution environment contains hard coded information that is received from the kernel - // * Items like, the block header and global variables fall within this category - // */ - //public readonly executionEnvironment: AvmExecutionEnvironment; - public l1GasLeft: number; + /** gas remaining of the gas allocated for a contract call */ public l2GasLeft: number; public daGasLeft: number; - + /** program counter */ public pc: number = 0; /** - * When an internal_call is invoked, the internal call stack is added to with the current pc + 1 - * When internal_return is invoked, the latest value is popped from the internal call stack and set to the pc. + * On INTERNALCALL, internal call stack is pushed to with the current pc + 1 + * On INTERNALRETURN, value is popped from the internal call stack and assigned to the pc. */ public internalCallStack: number[] = []; + /** Memory accessible to user code */ public readonly memory: TaggedMemory = new TaggedMemory(); - /** - * If an instruction triggers a halt, then it ends execution of the VM - */ + /** If an instruction triggers a halt, context execution ends */ public halted: boolean = false; - /** - * Signifies if the execution has reverted ( due to a revert instruction ) - */ + /** Flags whether the execution has reverted normally (this does nto cover exceptional halts) */ private reverted: boolean = false; - /** - * Output data must NOT be modified once it is set - */ + /** Output data must NOT be modified once it is set */ private output: Fr[] = []; /** - * Create a new avm context + * Create a new machine state * @param initialMachineState - The initial machine state passed to the avm */ constructor(initialMachineState: InitialAvmMachineState) { @@ -59,6 +47,9 @@ export class AvmMachineState { this.daGasLeft = initialMachineState.daGasLeft; } + /** + * Most instructions just increment PC before they complete + */ public incrementPc() { this.pc++; } @@ -84,7 +75,14 @@ export class AvmMachineState { this.output = output; } + /** + * Get a summary of execution results for a halted machine state + * @returns summary of execution results + */ public getResults(): AvmContractCallResults { + if (!this.halted) { + throw new Error('Execution results are not ready! Execution is ongoing.'); + } return new AvmContractCallResults(this.reverted, this.output); } } From 95a9dd83d2d1ba925fc5cc5c9b0c63fac8aad00d Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 21:25:46 +0000 Subject: [PATCH 08/16] comments --- .../acir-simulator/src/avm/avm_context.ts | 47 +++++++++++-------- .../src/avm/avm_machine_state.ts | 4 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 32b3075ef73..1cfc86853a8 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -2,8 +2,6 @@ import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { assert } from 'console'; - import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; @@ -13,6 +11,9 @@ import { AvmWorldStateJournal } from './journal/journal.js'; import { type Instruction, InstructionExecutionError } from './opcodes/index.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; +import { assert } from 'console'; + + /** * Avm Context manages the state and execution of the AVM */ @@ -61,7 +62,8 @@ export class AvmContext { /** * Set instructions directly (then can skip bytecode decoding) - * For testing purposes only! + * Warning: FOR TESTING PURPOSES ONLY! + * @param instructions - The decoded instructions to inject into this context */ setInstructions(instructions: Instruction[]) { this.instructions = instructions; @@ -69,22 +71,22 @@ export class AvmContext { /** * Execute the contract code within the current context. - * - * - Retrieve and decode bytecode - * - Interpret the bytecode - * - Execute - * */ async execute(): Promise { // Cannot execute empty contract or uninitialized context assert(this.instructions.length > 0); try { + // Execute instruction pointed to by the current program counter + // continuing until the machine state signifies a halt while (!this.machineState.halted) { const instruction = this.instructions[this.machineState.pc]; assert(!!instruction); // This should never happen this.log(`Executing PC=${this.machineState.pc}: ${instruction.toString()}`); + // Execute the instruction. + // Normal returns and reverts will return normally here. + // "Exceptional halts" will throw. await instruction.execute(this); if (this.machineState.pc >= this.instructions.length) { @@ -93,7 +95,7 @@ export class AvmContext { } } - // return results for processing by calling context + // Return results for processing by calling context const results = this.machineState.getResults(); this.log(`Context execution results: ${results.toString()}`); return results; @@ -104,7 +106,8 @@ export class AvmContext { throw e; } - // Exceptional halts cannot return data + // Return results for processing by calling context + // Note: "exceptional halts" cannot return data const results = new AvmContractCallResults(/*reverted=*/ true, /*output*/ [], /*revertReason=*/ e); this.log(`Context execution results: ${results.toString()}`); return results; @@ -112,14 +115,16 @@ export class AvmContext { } /** - * Prepare a new AVM context that will be ready for an external call + * Prepare a new AVM context that will be ready for an external/nested call * - Fork the world state journal - * - Set the correct execution environment variables for a call + * - Derive an execution environment from the caller/parent * - Alter both address and storageAddress * - * @param address - The contract to call - * @param parentEnvironment - The current execution environment - * @param parentWorldState - The current world state (journal) + * @param address - The contract instance to initialize a context for + * @param calldata - Data/arguments for nested call + * @param parentEnvironment - The caller/parent environment + * @param initialMachineState - The initial machine state (derived from call instruction args) + * @param parentWorldState - The caller/parent world state (journal) * @returns new AvmContext instance */ public static async createNestedContractCallContext( @@ -137,14 +142,16 @@ export class AvmContext { } /** - * Prepare a new AVM context that will be ready for an external static call + * Prepare a new AVM context that will be ready for an external/nested static call * - Fork the world state journal - * - Set the correct execution environment variables for a call + * - Derive an execution environment from the caller/parent * - Alter both address and storageAddress * - * @param address - The contract to call - * @param parentEnvironment - The current execution environment - * @param parentWorldState - The current world state (journal) + * @param address - The contract instance to initialize a context for + * @param calldata - Data/arguments for nested call + * @param parentEnvironment - The caller/parent environment + * @param initialMachineState - The initial machine state (derived from call instruction args) + * @param parentWorldState - The caller/parent world state (journal) * @returns new AvmContext instance */ public static async createNestedStaticCallContext( diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index fba8854bf6e..7dfbab598ce 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -29,9 +29,9 @@ export class AvmMachineState { /** Memory accessible to user code */ public readonly memory: TaggedMemory = new TaggedMemory(); - /** If an instruction triggers a halt, context execution ends */ + /** Signifies that execution should end */ public halted: boolean = false; - /** Flags whether the execution has reverted normally (this does nto cover exceptional halts) */ + /** Signifies that execution has reverted normally (this does not cover exceptional halts) */ private reverted: boolean = false; /** Output data must NOT be modified once it is set */ From 67e4fdc3545890d2c39c531958cb27b0536acccf Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 22:11:35 +0000 Subject: [PATCH 09/16] cleanup --- .../acir-simulator/src/avm/avm_context.ts | 44 +++++++++++-------- .../src/avm/avm_machine_state.ts | 11 +++-- yarn-project/acir-simulator/src/avm/errors.ts | 2 +- .../acir-simulator/src/avm/index.test.ts | 4 +- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 1cfc86853a8..8e43a968e89 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -5,7 +5,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; -import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeFoundInterpreterError } from './errors.js'; +import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeForContractError } from './errors.js'; import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; import { AvmWorldStateJournal } from './journal/journal.js'; import { type Instruction, InstructionExecutionError } from './opcodes/index.js'; @@ -30,6 +30,7 @@ export class AvmContext { /** Stage data for public kernel (1-kernel-per-call). * Shouldn't be necessary once kernel processes an entire AVM Session. */ + // TODO(4293): Integrate simulator with public kernel //private nestedExecutions: PublicExecutionResult[] = []; constructor( @@ -46,20 +47,6 @@ export class AvmContext { this.instructions = []; } - async init() { - // NOTE: the following is mocked as getPublicBytecode does not exist yet - const selector = new FunctionSelector(0); - const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode(this.environment.address, selector); - - // This assumes that we will not be able to send messages to accounts without code - // Pending classes and instances impl details - if (!bytecode) { - throw new NoBytecodeFoundInterpreterError(this.environment.address); - } - - this.instructions = decodeFromBytecode(bytecode); - } - /** * Set instructions directly (then can skip bytecode decoding) * Warning: FOR TESTING PURPOSES ONLY! @@ -72,7 +59,11 @@ export class AvmContext { /** * Execute the contract code within the current context. */ - async execute(): Promise { + async execute(skipBytecodeFetch: boolean): Promise { + if (!skipBytecodeFetch) { + // TODO(4409): decide what happens if contract instance does not exist (has no code) + await this.fetchAndDecodeBytecode(); + } // Cannot execute empty contract or uninitialized context assert(this.instructions.length > 0); @@ -114,6 +105,23 @@ export class AvmContext { } } + /** + * Fetch contract bytecode from world state and decode into executable instructions. + */ + private async fetchAndDecodeBytecode() { + // NOTE: the following is mocked as getPublicBytecode does not exist yet + const selector = new FunctionSelector(0); + const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode(this.environment.address, selector); + + // This assumes that we will not be able to send messages to accounts without code + // Pending classes and instances impl details + if (!bytecode) { + throw new NoBytecodeForContractError(this.environment.address); + } + + this.instructions = decodeFromBytecode(bytecode); + } + /** * Prepare a new AVM context that will be ready for an external/nested call * - Fork the world state journal @@ -137,7 +145,7 @@ export class AvmContext { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedCall(address, calldata); const forkedState = parentWorldState.fork(); const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); - await nestedContext.init(); + await nestedContext.fetchAndDecodeBytecode(); return nestedContext; } @@ -164,7 +172,7 @@ export class AvmContext { const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedStaticCall(address, calldata); const forkedState = parentWorldState.fork(); const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); - await nestedContext.init(); + await nestedContext.fetchAndDecodeBytecode(); return nestedContext; } } diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 7dfbab598ce..d494b19ad03 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -3,6 +3,9 @@ import { Fr } from '@aztec/circuits.js'; import { TaggedMemory } from './avm_memory_types.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; +/** + * A few fields of machine state are initialized from AVM session inputs or call instruction arguments + */ export type InitialAvmMachineState = { l1GasLeft: number; l2GasLeft: number; @@ -29,11 +32,13 @@ export class AvmMachineState { /** Memory accessible to user code */ public readonly memory: TaggedMemory = new TaggedMemory(); - /** Signifies that execution should end */ + /** + * Signals that execution should end. + * AvmContext execution continues executing instructions until the machine state signals "halted" + * */ public halted: boolean = false; - /** Signifies that execution has reverted normally (this does not cover exceptional halts) */ + /** Signals that execution has reverted normally (this does not cover exceptional halts) */ private reverted: boolean = false; - /** Output data must NOT be modified once it is set */ private output: Fr[] = []; diff --git a/yarn-project/acir-simulator/src/avm/errors.ts b/yarn-project/acir-simulator/src/avm/errors.ts index c97258e74d8..68b24e7354d 100644 --- a/yarn-project/acir-simulator/src/avm/errors.ts +++ b/yarn-project/acir-simulator/src/avm/errors.ts @@ -10,7 +10,7 @@ export abstract class AvmExecutionError extends Error { } } -export class NoBytecodeFoundInterpreterError extends AvmExecutionError { +export class NoBytecodeForContractError extends AvmExecutionError { constructor(contractAddress: AztecAddress) { super(`No bytecode found at: ${contractAddress}`); this.name = 'NoBytecodeFoundInterpreterError'; diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 6285c8e0eb7..c370708fcd6 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -40,7 +40,7 @@ describe('avm', () => { // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - await context.init(); + await context.fetchAndDecodeBytecode(); // Execute AVM const results = await context.execute(); @@ -67,7 +67,7 @@ describe('avm', () => { // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - await context.init(); + await context.fetchAndDecodeBytecode(); // Execute AVM const results = await context.execute(); From 42f97e9ae7d5f98471434225641a1380fdb9e00d Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 22:13:39 +0000 Subject: [PATCH 10/16] comments --- .../acir-simulator/src/avm/avm_message_call_result.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts index 2003374d632..3b7b4e87a0f 100644 --- a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts +++ b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts @@ -1,12 +1,13 @@ import { Fr } from '@aztec/foundation/fields'; /** - * AVM contract call results. + * Results of an contract call's execution in the AVM. */ export class AvmContractCallResults { public readonly reverted: boolean; public readonly output: Fr[]; + /** For exceptional halts */ public readonly revertReason: Error | undefined; constructor(reverted: boolean, output: Fr[], revertReason?: Error) { @@ -15,6 +16,9 @@ export class AvmContractCallResults { this.revertReason = revertReason; } + /** + * Generate a string representation of call results. + */ toString(): string { let resultsStr = `reverted: ${this.reverted}, output: ${this.output}`; if (this.revertReason) { From 0ebf063c7c407ae38ef07096fe6556776cbf57d7 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 22:36:39 +0000 Subject: [PATCH 11/16] cleanup tag checks --- .../acir-simulator/src/avm/avm_context.ts | 2 +- .../src/avm/avm_memory_types.ts | 32 ++++++++ yarn-project/acir-simulator/src/avm/errors.ts | 21 ++++++ .../acir-simulator/src/avm/opcodes/bitwise.ts | 12 +-- .../src/avm/opcodes/comparators.ts | 6 +- .../src/avm/opcodes/instruction.ts | 74 +++++++------------ .../src/avm/opcodes/memory.test.ts | 4 +- 7 files changed, 93 insertions(+), 58 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 8e43a968e89..9f6e7c82a30 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -92,7 +92,7 @@ export class AvmContext { return results; } catch (e) { this.log('Exceptional halt'); - if (!(e instanceof AvmExecutionError || e instanceof InstructionExecutionError)) { + if (!(e instanceof AvmExecutionError)) { this.log(`Unknown error thrown by avm: ${e}`); throw e; } diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 40c02b65463..3c689d35f64 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -1,5 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; +import { TagCheckError } from './errors.js'; + import { strict as assert } from 'assert'; export abstract class MemoryValue { @@ -268,6 +270,35 @@ export class TaggedMemory { return TaggedMemory.getTag(this._mem[offset]); } + + /** + * Check that the memory at the given offset matches the specified tag. + */ + public checkTag(tag: TypeTag, offset: number) { + if (this.getTag(offset) !== tag) { + const error = `Offset ${offset} has tag ${TypeTag[this.getTag(offset)]}, expected ${TypeTag[tag]}`; + throw new TagCheckError(offset, TypeTag[this.getTag(offset)], TypeTag[tag]); + } + } + + /** + * Check tags for memory at all of the specified offsets. + */ + public checkTags(tag: TypeTag, ...offsets: number[]) { + for (const offset of offsets) { + this.checkTag(tag, offset); + } + } + + /** + * Check tags for all memory in the specified range. + */ + public checkTagsRange(tag: TypeTag, startOffset: number, size: number) { + for (let offset = startOffset; offset < startOffset + size; offset++) { + this.checkTag(tag, offset); + } + } + // TODO: this might be slow, but I don't want to have the types know of their tags. // It might be possible to have a map. public static getTag(v: MemoryValue | undefined): TypeTag { @@ -311,3 +342,4 @@ export class TaggedMemory { } } } + diff --git a/yarn-project/acir-simulator/src/avm/errors.ts b/yarn-project/acir-simulator/src/avm/errors.ts index 68b24e7354d..65d47904939 100644 --- a/yarn-project/acir-simulator/src/avm/errors.ts +++ b/yarn-project/acir-simulator/src/avm/errors.ts @@ -1,4 +1,5 @@ import { AztecAddress } from '@aztec/circuits.js'; +import { type TypeTag } from './avm_memory_types.js'; /** * Avm-specific errors should derive from this @@ -27,3 +28,23 @@ export class InvalidProgramCounterError extends AvmExecutionError { this.name = 'InvalidProgramCounterError'; } } + +/** + * Error thrown during an instruction's execution (during its execute()). + */ +export class InstructionExecutionError extends AvmExecutionError { + constructor(message: string) { + super(message); + this.name = 'InstructionExecutionError'; + } +} + +/** + * Error thrown during an instruction's execution (during its execute()). + */ +export class TagCheckError extends AvmExecutionError { + constructor(offset: number, gotTag: string, expectedTag: string) { + super(`Memory offset ${offset} has tag ${gotTag}, expected ${expectedTag}`); + this.name = 'TagCheckError'; + } +} \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 36ef547396a..afbe69da907 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -13,7 +13,7 @@ export class And extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); const b = context.machineState.memory.getAs(this.bOffset); @@ -34,7 +34,7 @@ export class Or extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); const b = context.machineState.memory.getAs(this.bOffset); @@ -55,7 +55,7 @@ export class Xor extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); const b = context.machineState.memory.getAs(this.bOffset); @@ -76,7 +76,7 @@ export class Not extends TwoOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); @@ -96,7 +96,7 @@ export class Shl extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); const b = context.machineState.memory.getAs(this.bOffset); @@ -117,7 +117,7 @@ export class Shr extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.getAs(this.aOffset); const b = context.machineState.memory.getAs(this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 2ad5ecd0e7e..b817ae539dd 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -12,7 +12,7 @@ export class Eq extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.get(this.aOffset); const b = context.machineState.memory.get(this.bOffset); @@ -34,7 +34,7 @@ export class Lt extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.get(this.aOffset); const b = context.machineState.memory.get(this.bOffset); @@ -56,7 +56,7 @@ export class Lte extends ThreeOperandInstruction { } async execute(context: AvmContext): Promise { - Instruction.checkTags(context.machineState, this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); const a = context.machineState.memory.get(this.aOffset); const b = context.machineState.memory.get(this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 10c56297f96..3f4cf27f7de 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,28 +1,46 @@ import { assert } from 'console'; import type { AvmContext } from '../avm_context.js'; -import { AvmMachineState } from '../avm_machine_state.js'; -import { TypeTag } from '../avm_memory_types.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; /** * Parent class for all AVM instructions. - * It's most important aspects are execution and (de)serialization. + * It's most important aspects are execute and (de)serialize. */ export abstract class Instruction { + /** + * Execute the instruction. + * Instruction sub-classes must implement this. + * As an AvmContext executes its contract code, it calls this function for + * each instruction until the machine state signals "halted". + * @param context - The AvmContext in which the instruction executes. + */ public abstract execute(context: AvmContext): Promise; - static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { - for (const offset of offsets) { - checkTag(machineState, tag, offset); + /** + * Generate a string representation of the instruction including + * the instruction sub-class name all of its flags and operands. + * @returns Thee string representation. + */ + public toString(): string { + let instructionStr = this.constructor.name + ': '; + // assumes that all properties are flags or operands + for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { + instructionStr += `${prop}:${this[prop].toString()}, `; } + return instructionStr; } - static checkTagsRange(machineState: AvmMachineState, tag: TypeTag, startOffset: number, size: number) { - for (let offset = startOffset; offset < startOffset + size; offset++) { - checkTag(machineState, tag, offset); - } + /** + * Serialize the instruction to a Buffer according to its wire format specified in its subclass. + * If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}. + * @param this - The instruction to serialize. + * @returns The serialized instruction. + */ + public serialize(this: any): Buffer { + assert(this instanceof Instruction); + return serialize(this.constructor.wireFormat, this); } /** @@ -40,40 +58,4 @@ export abstract class Instruction { const args = res.slice(1) as ConstructorParameters; // Remove opcode. return new this(...args); } - - public serialize(this: any): Buffer { - assert(this instanceof Instruction); - return serialize(this.constructor.wireFormat, this); - } - - /** - * Generate a string representation of the instruction including - * the instruction sub-class name all of its flags and operands. - * @returns Thee string representation. - */ - public toString(): string { - let instructionStr = this.constructor.name + ': '; - // assumes that all properties are flags or operands - for (const prop of Object.getOwnPropertyNames(this) as (keyof Instruction)[]) { - instructionStr += `${prop}:${this[prop].toString()}, `; - } - return instructionStr; - } -} - -/** - * Checks that the memory at the given offset has the given tag. - */ -function checkTag(machineState: AvmMachineState, tag: TypeTag, offset: number) { - if (machineState.memory.getTag(offset) !== tag) { - const error = `Offset ${offset} has tag ${TypeTag[machineState.memory.getTag(offset)]}, expected ${TypeTag[tag]}`; - throw new InstructionExecutionError(error); - } -} - -export class InstructionExecutionError extends Error { - constructor(message: string) { - super(message); - this.name = 'InstructionExecutionError'; - } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 1709e99ffe2..451665299d7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -6,8 +6,8 @@ import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; -import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; +import { TagCheckError } from '../errors.js'; describe('Memory instructions', () => { let context: AvmContext; @@ -74,7 +74,7 @@ describe('Memory instructions', () => { for (const tag of [TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID]) { await expect( new Set(/*indirect=*/ 0, /*inTag=*/ tag, /*value=*/ 1234n, /*offset=*/ 1).execute(context), - ).rejects.toThrow(InstructionExecutionError); + ).rejects.toThrow(TagCheckError); } }); }); From a6252a0a7661b06adc45c5a3fe74f36a2f2ec2ae Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 22:41:17 +0000 Subject: [PATCH 12/16] prettier --- yarn-project/acir-simulator/src/avm/avm_context.ts | 7 +++---- yarn-project/acir-simulator/src/avm/avm_memory_types.ts | 7 ++----- yarn-project/acir-simulator/src/avm/errors.ts | 5 ++--- yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts | 1 - yarn-project/acir-simulator/src/avm/opcodes/comparators.ts | 1 - yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts | 2 +- 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 9f6e7c82a30..b3c70027000 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -2,18 +2,17 @@ import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import { assert } from 'console'; + import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; import { AvmContractCallResults } from './avm_message_call_result.js'; import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeForContractError } from './errors.js'; import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; import { AvmWorldStateJournal } from './journal/journal.js'; -import { type Instruction, InstructionExecutionError } from './opcodes/index.js'; +import { type Instruction } from './opcodes/index.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; -import { assert } from 'console'; - - /** * Avm Context manages the state and execution of the AVM */ diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 3c689d35f64..9515ae59ce0 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -1,9 +1,9 @@ import { Fr } from '@aztec/foundation/fields'; -import { TagCheckError } from './errors.js'; - import { strict as assert } from 'assert'; +import { TagCheckError } from './errors.js'; + export abstract class MemoryValue { public abstract add(rhs: MemoryValue): MemoryValue; public abstract sub(rhs: MemoryValue): MemoryValue; @@ -270,13 +270,11 @@ export class TaggedMemory { return TaggedMemory.getTag(this._mem[offset]); } - /** * Check that the memory at the given offset matches the specified tag. */ public checkTag(tag: TypeTag, offset: number) { if (this.getTag(offset) !== tag) { - const error = `Offset ${offset} has tag ${TypeTag[this.getTag(offset)]}, expected ${TypeTag[tag]}`; throw new TagCheckError(offset, TypeTag[this.getTag(offset)], TypeTag[tag]); } } @@ -342,4 +340,3 @@ export class TaggedMemory { } } } - diff --git a/yarn-project/acir-simulator/src/avm/errors.ts b/yarn-project/acir-simulator/src/avm/errors.ts index 65d47904939..735abb33df5 100644 --- a/yarn-project/acir-simulator/src/avm/errors.ts +++ b/yarn-project/acir-simulator/src/avm/errors.ts @@ -1,5 +1,4 @@ import { AztecAddress } from '@aztec/circuits.js'; -import { type TypeTag } from './avm_memory_types.js'; /** * Avm-specific errors should derive from this @@ -40,11 +39,11 @@ export class InstructionExecutionError extends AvmExecutionError { } /** - * Error thrown during an instruction's execution (during its execute()). + * Error thrown on failed AVM memory tag check. */ export class TagCheckError extends AvmExecutionError { constructor(offset: number, gotTag: string, expectedTag: string) { super(`Memory offset ${offset} has tag ${gotTag}, expected ${expectedTag}`); this.name = 'TagCheckError'; } -} \ No newline at end of file +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index afbe69da907..e6fbbce3863 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,7 +1,6 @@ import type { AvmContext } from '../avm_context.js'; import { IntegralValue } from '../avm_memory_types.js'; import { Opcode } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js'; export class And extends ThreeOperandInstruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index b817ae539dd..3f896248738 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,6 +1,5 @@ import type { AvmContext } from '../avm_context.js'; import { Opcode } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; export class Eq extends ThreeOperandInstruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 451665299d7..3784ae08da0 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -4,10 +4,10 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; +import { TagCheckError } from '../errors.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; -import { TagCheckError } from '../errors.js'; describe('Memory instructions', () => { let context: AvmContext; From 20f2ed33c2af84969896a41d29f079329878dc29 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 23:07:48 +0000 Subject: [PATCH 13/16] fixes after refactor --- yarn-project/acir-simulator/src/avm/avm_context.ts | 8 +++++--- yarn-project/acir-simulator/src/avm/index.test.ts | 2 -- yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts | 2 +- .../acir-simulator/src/avm/opcodes/comparators.test.ts | 8 ++++---- .../acir-simulator/src/avm/opcodes/control_flow.test.ts | 2 +- .../acir-simulator/src/avm/opcodes/control_flow.ts | 3 ++- .../acir-simulator/src/avm/opcodes/memory.test.ts | 4 ++-- yarn-project/acir-simulator/src/avm/opcodes/memory.ts | 3 ++- yarn-project/acir-simulator/src/avm/opcodes/storage.ts | 3 ++- 9 files changed, 19 insertions(+), 16 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index b3c70027000..12f22fa9c5c 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -47,7 +47,7 @@ export class AvmContext { } /** - * Set instructions directly (then can skip bytecode decoding) + * Set instructions directly (execute() will then skip bytecode fetch & decode) * Warning: FOR TESTING PURPOSES ONLY! * @param instructions - The decoded instructions to inject into this context */ @@ -58,8 +58,10 @@ export class AvmContext { /** * Execute the contract code within the current context. */ - async execute(skipBytecodeFetch: boolean): Promise { - if (!skipBytecodeFetch) { + async execute(): Promise { + if (this.instructions.length == 0) { + // Note: contract code may have been loaded for a test via setInstructions in which case + // fetch is skipped // TODO(4409): decide what happens if contract instance does not exist (has no code) await this.fetchAndDecodeBytecode(); } diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index c370708fcd6..e34e943b588 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -40,7 +40,6 @@ describe('avm', () => { // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - await context.fetchAndDecodeBytecode(); // Execute AVM const results = await context.execute(); @@ -67,7 +66,6 @@ describe('avm', () => { // Initialize AVM context const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - await context.fetchAndDecodeBytecode(); // Execute AVM const results = await context.execute(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index e6fbbce3863..9161c8bb22c 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -75,7 +75,7 @@ export class Not extends TwoOperandInstruction { } async execute(context: AvmContext): Promise { - context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); + context.machineState.memory.checkTags(this.inTag, this.aOffset); const a = context.machineState.memory.getAs(this.aOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index 8eb016d00eb..03987d039ff 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -4,7 +4,7 @@ import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Eq, Lt, Lte } from './comparators.js'; -import { InstructionExecutionError } from './instruction.js'; +import { TagCheckError } from '../errors.js'; describe('Comparators', () => { let context: AvmContext; @@ -74,7 +74,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); } }); }); @@ -138,7 +138,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); } }); }); @@ -202,7 +202,7 @@ describe('Comparators', () => { ]; for (const o of ops) { - await expect(() => o.execute(context)).rejects.toThrow(InstructionExecutionError); + await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); } }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 8fdb6759265..728b0afcbba 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -6,7 +6,7 @@ import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; -import { InstructionExecutionError } from './instruction.js'; +import { InstructionExecutionError } from '../errors.js'; describe('Control Flow Opcodes', () => { let context: AvmContext; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 2fdfced13ef..a3a8faaf619 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,7 +1,8 @@ import type { AvmContext } from '../avm_context.js'; import { IntegralValue } from '../avm_memory_types.js'; +import { InstructionExecutionError } from '../errors.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; -import { Instruction, InstructionExecutionError } from './instruction.js'; +import { Instruction } from './instruction.js'; export class Return extends Instruction { static type: string = 'RETURN'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 3784ae08da0..9ff147346f8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -4,7 +4,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; -import { TagCheckError } from '../errors.js'; +import { InstructionExecutionError } from '../errors.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; @@ -74,7 +74,7 @@ describe('Memory instructions', () => { for (const tag of [TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID]) { await expect( new Set(/*indirect=*/ 0, /*inTag=*/ tag, /*value=*/ 1234n, /*offset=*/ 1).execute(context), - ).rejects.toThrow(TagCheckError); + ).rejects.toThrow(InstructionExecutionError); } }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index ee2ca0cc0b7..4e43edc6868 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,7 +1,8 @@ import type { AvmContext } from '../avm_context.js'; import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; +import { InstructionExecutionError } from '../errors.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; -import { Instruction, InstructionExecutionError } from './instruction.js'; +import { Instruction } from './instruction.js'; import { TwoOperandInstruction } from './instruction_impl.js'; export class Set extends Instruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index 35af2206ae5..f76de3822d5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -3,7 +3,8 @@ import { Fr } from '@aztec/foundation/fields'; import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; -import { Instruction, InstructionExecutionError } from './instruction.js'; +import { Instruction } from './instruction.js'; +import { InstructionExecutionError } from '../errors.js'; abstract class BaseStorageInstruction extends Instruction { // Informs (de)serialization. See Instruction.deserialize. From 09cba7f3830df7953928f39a25b9be4488340c23 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Sun, 4 Feb 2024 23:08:17 +0000 Subject: [PATCH 14/16] formatting:fix --- yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts | 2 +- .../acir-simulator/src/avm/opcodes/control_flow.test.ts | 2 +- yarn-project/acir-simulator/src/avm/opcodes/storage.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index 03987d039ff..0fb74b4bb51 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -2,9 +2,9 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; +import { TagCheckError } from '../errors.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Eq, Lt, Lte } from './comparators.js'; -import { TagCheckError } from '../errors.js'; describe('Comparators', () => { let context: AvmContext; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 728b0afcbba..47c31b24c6c 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -4,9 +4,9 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; +import { InstructionExecutionError } from '../errors.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; -import { InstructionExecutionError } from '../errors.js'; describe('Control Flow Opcodes', () => { let context: AvmContext; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index f76de3822d5..3d59c5c57c6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -2,9 +2,9 @@ import { Fr } from '@aztec/foundation/fields'; import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; +import { InstructionExecutionError } from '../errors.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; -import { InstructionExecutionError } from '../errors.js'; abstract class BaseStorageInstruction extends Instruction { // Informs (de)serialization. See Instruction.deserialize. From df26baa5977e3e97d1d7196167c41b3eaa955937 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 5 Feb 2024 12:36:51 +0000 Subject: [PATCH 15/16] WIP proposed changes --- .../acir-simulator/src/avm/avm_context.ts | 151 ++---------------- .../src/avm/avm_machine_state.ts | 20 ++- .../{index.test.ts => avm_simulator.test.ts} | 16 +- .../acir-simulator/src/avm/avm_simulator.ts | 86 ++++++++++ .../src/avm/opcodes/arithmetic.test.ts | 11 +- .../src/avm/opcodes/external_calls.ts | 28 +--- .../acir-simulator/src/avm/opcodes/index.ts | 3 +- .../serialization/bytecode_serialization.ts | 4 +- 8 files changed, 137 insertions(+), 182 deletions(-) rename yarn-project/acir-simulator/src/avm/{index.test.ts => avm_simulator.test.ts} (85%) create mode 100644 yarn-project/acir-simulator/src/avm/avm_simulator.ts diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 12f22fa9c5c..cc15e1c4b66 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -1,126 +1,25 @@ -import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; - -import { assert } from 'console'; import { AvmExecutionEnvironment } from './avm_execution_environment.js'; -import { AvmMachineState, InitialAvmMachineState } from './avm_machine_state.js'; -import { AvmContractCallResults } from './avm_message_call_result.js'; -import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeForContractError } from './errors.js'; -import { initExecutionEnvironment, initInitialMachineState } from './fixtures/index.js'; +import { AvmMachineState } from './avm_machine_state.js'; import { AvmWorldStateJournal } from './journal/journal.js'; -import { type Instruction } from './opcodes/index.js'; -import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; /** * Avm Context manages the state and execution of the AVM */ export class AvmContext { /** Contains constant variables provided by the kernel */ - public environment: AvmExecutionEnvironment = initExecutionEnvironment(); + public environment: AvmExecutionEnvironment; /** VM state that is modified on an instruction-by-instruction basis */ public machineState: AvmMachineState; /** Manages mutable state during execution - (caching, fetching) */ public worldState: AvmWorldStateJournal; - /** The public contract code corresponding to this context's contract class */ - private instructions: Instruction[]; - - /** Stage data for public kernel (1-kernel-per-call). - * Shouldn't be necessary once kernel processes an entire AVM Session. */ - // TODO(4293): Integrate simulator with public kernel - //private nestedExecutions: PublicExecutionResult[] = []; - - constructor( - worldState: AvmWorldStateJournal, - environment: AvmExecutionEnvironment = initExecutionEnvironment(), - /** Some initial values for machine state */ - initialMachineState: InitialAvmMachineState = initInitialMachineState(), - private log = createDebugLogger('aztec:avm_simulator:avm_context'), - ) { - this.worldState = worldState; + constructor(environment: AvmExecutionEnvironment, machineState: AvmMachineState, worldState: AvmWorldStateJournal) { this.environment = environment; - this.machineState = new AvmMachineState(initialMachineState); - // Bytecode is fetched and instructions are decoded in async init() - this.instructions = []; - } - - /** - * Set instructions directly (execute() will then skip bytecode fetch & decode) - * Warning: FOR TESTING PURPOSES ONLY! - * @param instructions - The decoded instructions to inject into this context - */ - setInstructions(instructions: Instruction[]) { - this.instructions = instructions; - } - - /** - * Execute the contract code within the current context. - */ - async execute(): Promise { - if (this.instructions.length == 0) { - // Note: contract code may have been loaded for a test via setInstructions in which case - // fetch is skipped - // TODO(4409): decide what happens if contract instance does not exist (has no code) - await this.fetchAndDecodeBytecode(); - } - // Cannot execute empty contract or uninitialized context - assert(this.instructions.length > 0); - - try { - // Execute instruction pointed to by the current program counter - // continuing until the machine state signifies a halt - while (!this.machineState.halted) { - const instruction = this.instructions[this.machineState.pc]; - assert(!!instruction); // This should never happen - - this.log(`Executing PC=${this.machineState.pc}: ${instruction.toString()}`); - // Execute the instruction. - // Normal returns and reverts will return normally here. - // "Exceptional halts" will throw. - await instruction.execute(this); - - if (this.machineState.pc >= this.instructions.length) { - this.log('Passed end of program!'); - throw new InvalidProgramCounterError(this.machineState.pc, /*max=*/ this.instructions.length); - } - } - - // Return results for processing by calling context - const results = this.machineState.getResults(); - this.log(`Context execution results: ${results.toString()}`); - return results; - } catch (e) { - this.log('Exceptional halt'); - if (!(e instanceof AvmExecutionError)) { - this.log(`Unknown error thrown by avm: ${e}`); - throw e; - } - - // Return results for processing by calling context - // Note: "exceptional halts" cannot return data - const results = new AvmContractCallResults(/*reverted=*/ true, /*output*/ [], /*revertReason=*/ e); - this.log(`Context execution results: ${results.toString()}`); - return results; - } - } - - /** - * Fetch contract bytecode from world state and decode into executable instructions. - */ - private async fetchAndDecodeBytecode() { - // NOTE: the following is mocked as getPublicBytecode does not exist yet - const selector = new FunctionSelector(0); - const bytecode = await this.worldState.hostStorage.contractsDb.getBytecode(this.environment.address, selector); - - // This assumes that we will not be able to send messages to accounts without code - // Pending classes and instances impl details - if (!bytecode) { - throw new NoBytecodeForContractError(this.environment.address); - } - - this.instructions = decodeFromBytecode(bytecode); + this.machineState = machineState; + this.worldState = worldState; } /** @@ -131,23 +30,14 @@ export class AvmContext { * * @param address - The contract instance to initialize a context for * @param calldata - Data/arguments for nested call - * @param parentEnvironment - The caller/parent environment * @param initialMachineState - The initial machine state (derived from call instruction args) - * @param parentWorldState - The caller/parent world state (journal) * @returns new AvmContext instance */ - public static async createNestedContractCallContext( - address: AztecAddress, - calldata: Fr[], - parentEnvironment: AvmExecutionEnvironment, - initialMachineState: InitialAvmMachineState, - parentWorldState: AvmWorldStateJournal, - ): Promise { - const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedCall(address, calldata); - const forkedState = parentWorldState.fork(); - const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); - await nestedContext.fetchAndDecodeBytecode(); - return nestedContext; + public createNestedContractCallContext(address: AztecAddress, calldata: Fr[]): AvmContext { + const newExecutionEnvironment = this.environment.deriveEnvironmentForNestedCall(address, calldata); + const forkedState = this.worldState.fork(); + const machineState = AvmMachineState.fromState(this.machineState); + return new AvmContext(newExecutionEnvironment, machineState, forkedState); } /** @@ -158,22 +48,13 @@ export class AvmContext { * * @param address - The contract instance to initialize a context for * @param calldata - Data/arguments for nested call - * @param parentEnvironment - The caller/parent environment * @param initialMachineState - The initial machine state (derived from call instruction args) - * @param parentWorldState - The caller/parent world state (journal) * @returns new AvmContext instance */ - public static async createNestedStaticCallContext( - address: AztecAddress, - calldata: Fr[], - parentEnvironment: AvmExecutionEnvironment, - initialMachineState: InitialAvmMachineState, - parentWorldState: AvmWorldStateJournal, - ): Promise { - const newExecutionEnvironment = parentEnvironment.deriveEnvironmentForNestedStaticCall(address, calldata); - const forkedState = parentWorldState.fork(); - const nestedContext = new AvmContext(forkedState, newExecutionEnvironment, initialMachineState); - await nestedContext.fetchAndDecodeBytecode(); - return nestedContext; + public createNestedContractStaticCallContext(address: AztecAddress, calldata: Fr[]): AvmContext { + const newExecutionEnvironment = this.environment.deriveEnvironmentForNestedStaticCall(address, calldata); + const forkedState = this.worldState.fork(); + const machineState = AvmMachineState.fromState(this.machineState); + return new AvmContext(newExecutionEnvironment, machineState, forkedState); } } diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index d494b19ad03..28bef8c22d9 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -42,14 +42,18 @@ export class AvmMachineState { /** Output data must NOT be modified once it is set */ private output: Fr[] = []; - /** - * Create a new machine state - * @param initialMachineState - The initial machine state passed to the avm - */ - constructor(initialMachineState: InitialAvmMachineState) { - this.l1GasLeft = initialMachineState.l1GasLeft; - this.l2GasLeft = initialMachineState.l2GasLeft; - this.daGasLeft = initialMachineState.daGasLeft; + constructor(l1GasLeft: number, l2GasLeft: number, daGasLeft: number) { + this.l1GasLeft = l1GasLeft; + this.l2GasLeft = l2GasLeft; + this.daGasLeft = daGasLeft; + } + + public static fromState(state: InitialAvmMachineState): AvmMachineState { + return new AvmMachineState(state.l1GasLeft, state.l2GasLeft, state.daGasLeft); + } + + public static ZERO() { + return new AvmMachineState(/*l1GasLeft=*/ 0, /*l2GasLeft=*/ 0, /*daGasLeft=*/ 0); } /** diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts similarity index 85% rename from yarn-project/acir-simulator/src/avm/index.test.ts rename to yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index e34e943b588..35d9099b575 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -6,7 +6,9 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; import { AvmContext } from './avm_context.js'; +import { AvmMachineState } from './avm_machine_state.js'; import { TypeTag } from './avm_memory_types.js'; +import { AvmSimulator } from './avm_simulator.js'; import { initExecutionEnvironment } from './fixtures/index.js'; import { HostStorage } from './journal/host_storage.js'; import { AvmWorldStateJournal } from './journal/journal.js'; @@ -38,11 +40,8 @@ describe('avm', () => { jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); - // Initialize AVM context - const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - - // Execute AVM - const results = await context.execute(); + const context = new AvmContext(initExecutionEnvironment({ calldata }), AvmMachineState.ZERO(), journal); + const results = await new AvmSimulator(context).execute(); expect(results.reverted).toBe(false); @@ -64,11 +63,8 @@ describe('avm', () => { jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); - // Initialize AVM context - const context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - - // Execute AVM - const results = await context.execute(); + const context = new AvmContext(initExecutionEnvironment({ calldata }), AvmMachineState.ZERO(), journal); + const results = await new AvmSimulator(context).execute(); expect(results.reverted).toBe(false); diff --git a/yarn-project/acir-simulator/src/avm/avm_simulator.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.ts new file mode 100644 index 00000000000..842b86e62c5 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.ts @@ -0,0 +1,86 @@ +import { FunctionSelector } from '@aztec/circuits.js'; +// import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; + +import { strict as assert } from 'assert'; + +import type { AvmContext } from './avm_context.js'; +import { AvmContractCallResults } from './avm_message_call_result.js'; +import { AvmExecutionError, InvalidProgramCounterError, NoBytecodeForContractError } from './errors.js'; +import type { Instruction } from './opcodes/index.js'; +import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; + +export class AvmSimulator { + private log = createDebugLogger('todo-here'); + + constructor(private context: AvmContext) {} + + /** + * Execute the contract code within the current context. + */ + public async execute(): Promise { + const instructions = await this.fetchAndDecodeBytecode(); + // Cannot execute empty contract or uninitialized context + assert(instructions.length > 0); + return this.executeInstructions(instructions); + } + + public async executeInstructions(instructions: Instruction[]): Promise { + try { + // Execute instruction pointed to by the current program counter + // continuing until the machine state signifies a halt + while (!this.context.machineState.halted) { + const instruction = instructions[this.context.machineState.pc]; + assert(!!instruction); // This should never happen + + this.log(`Executing PC=${this.context.machineState.pc}: ${instruction.toString()}`); + // Execute the instruction. + // Normal returns and reverts will return normally here. + // "Exceptional halts" will throw. + await instruction.execute(this.context); + + if (this.context.machineState.pc >= instructions.length) { + this.log('Passed end of program!'); + throw new InvalidProgramCounterError(this.context.machineState.pc, /*max=*/ instructions.length); + } + } + + // Return results for processing by calling context + const results = this.context.machineState.getResults(); + this.log(`Context execution results: ${results.toString()}`); + return results; + } catch (e) { + this.log('Exceptional halt'); + if (!(e instanceof AvmExecutionError)) { + this.log(`Unknown error thrown by avm: ${e}`); + throw e; + } + + // Return results for processing by calling context + // Note: "exceptional halts" cannot return data + const results = new AvmContractCallResults(/*reverted=*/ true, /*output=*/ [], /*revertReason=*/ e); + this.log(`Context execution results: ${results.toString()}`); + return results; + } + } + + /** + * Fetch contract bytecode from world state and decode into executable instructions. + */ + private async fetchAndDecodeBytecode(): Promise { + // NOTE: the following is mocked as getPublicBytecode does not exist yet + const selector = new FunctionSelector(0); + const bytecode = await this.context.worldState.hostStorage.contractsDb.getBytecode( + this.context.environment.address, + selector, + ); + + // This assumes that we will not be able to send messages to accounts without code + // Pending classes and instances impl details + if (!bytecode) { + throw new NoBytecodeForContractError(this.context.environment.address); + } + + return decodeFromBytecode(bytecode); + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index e5425647ff9..7758d7f0322 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,17 +1,20 @@ -import { MockProxy, mock } from 'jest-mock-extended'; +import { mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; +import { AvmMachineState } from '../avm_machine_state.js'; import { Field, TypeTag } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { let context: AvmContext; - let journal: MockProxy; beforeEach(() => { - journal = mock(); - context = new AvmContext(journal); + const journal = mock(); + const machineState = AvmMachineState.ZERO(); + const env = initExecutionEnvironment(); + context = new AvmContext(env, machineState, journal); }); describe('Add', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 6baa09fd545..8ff20f6b24a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -1,8 +1,8 @@ import { Fr } from '@aztec/foundation/fields'; -import { AvmContext } from '../avm_context.js'; +import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initMachineState } from '../fixtures/index.js'; +import { AvmSimulator } from '../avm_simulator.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -38,19 +38,11 @@ export class Call extends Instruction { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment async execute(context: AvmContext): Promise { const callAddress = context.machineState.memory.getAs(this.addrOffset); - const calldata = context.machineState.memory - .getSlice(this.argsOffset, this.argsSize) - .map(f => new Fr(f.toBigInt())); + const calldata = context.machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => f.toFr()); - const nestedContext = await AvmContext.createNestedContractCallContext( - new Fr(callAddress.toBigInt()), - calldata, - context.environment, - initMachineState(), - context.worldState, - ); + const nestedContext = context.createNestedContractCallContext(callAddress.toFr(), calldata); - const nestedCallResults = await nestedContext.execute(); + const nestedCallResults = await new AvmSimulator(nestedContext).execute(); const success = !nestedCallResults.reverted; // We only take as much data as was specified in the return size -> TODO: should we be reverting here @@ -106,15 +98,9 @@ export class StaticCall extends Instruction { .getSlice(this.argsOffset, this.argsSize) .map(f => new Fr(f.toBigInt())); - const nestedContext = await AvmContext.createNestedStaticCallContext( - new Fr(callAddress.toBigInt()), - calldata, - context.environment, - initMachineState(), - context.worldState, - ); + const nestedContext = context.createNestedContractStaticCallContext(callAddress.toFr(), calldata); - const nestedCallResults = await nestedContext.execute(); + const nestedCallResults = await new AvmSimulator(nestedContext).execute(); const success = !nestedCallResults.reverted; // We only take as much data as was specified in the return size -> TODO: should we be reverting here diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index 2fa19f7b04a..d8dc52a8cf5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -5,7 +5,6 @@ export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; export * from './storage.js'; -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/4359): dependency cycle -// export * from './external_calls.js'; +export * from './external_calls.js'; export * from './environment_getters.js'; export * from './accrued_substate.js'; diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index 8b200e5800c..3690393c8d8 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -3,7 +3,7 @@ import { Address, And, BlockNumber, - CMov, // Call, + CMov, //Call, CalldataCopy, Cast, ChainId, @@ -35,7 +35,7 @@ import { Sender, Set, Shl, - Shr, // StaticCall, + Shr, //StaticCall, StorageAddress, Sub, Timestamp, From b574633281e97511be553e57a3b77be703054544 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 5 Feb 2024 16:57:01 +0000 Subject: [PATCH 16/16] fix all the things --- .../src/avm/avm_context.test.ts | 69 ++++--------------- .../acir-simulator/src/avm/avm_context.ts | 45 ++++++------ .../src/avm/avm_machine_state.ts | 4 -- .../src/avm/avm_simulator.test.ts | 28 ++------ .../acir-simulator/src/avm/avm_simulator.ts | 15 ++-- .../acir-simulator/src/avm/fixtures/index.ts | 43 ++++++++---- .../src/avm/opcodes/accrued_substate.test.ts | 13 +--- .../src/avm/opcodes/arithmetic.test.ts | 11 +-- .../src/avm/opcodes/bitwise.test.ts | 10 +-- .../src/avm/opcodes/comparators.test.ts | 10 +-- .../src/avm/opcodes/control_flow.test.ts | 8 +-- .../avm/opcodes/environment_getters.test.ts | 17 +---- .../src/avm/opcodes/external_calls.test.ts | 19 +++-- .../src/avm/opcodes/memory.test.ts | 17 ++--- .../src/avm/opcodes/storage.test.ts | 9 ++- .../bytecode_serialization.test.ts | 42 ++++++++++- .../serialization/bytecode_serialization.ts | 28 +++++--- 17 files changed, 175 insertions(+), 213 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.test.ts b/yarn-project/acir-simulator/src/avm/avm_context.test.ts index a4c270dfb2e..6f198fa0bb8 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.test.ts @@ -1,59 +1,18 @@ -import { Fr } from '@aztec/foundation/fields'; - -import { MockProxy, mock } from 'jest-mock-extended'; - -import { AvmContext } from './avm_context.js'; -import { TypeTag } from './avm_memory_types.js'; -import { InvalidProgramCounterError } from './errors.js'; -import { initExecutionEnvironment } from './fixtures/index.js'; -import { AvmWorldStateJournal } from './journal/journal.js'; -import { Add } from './opcodes/arithmetic.js'; -import { Jump, Return } from './opcodes/control_flow.js'; -import { Instruction } from './opcodes/instruction.js'; -import { CalldataCopy } from './opcodes/memory.js'; - -describe('avm context', () => { - let context: AvmContext; - let journal: MockProxy; - - beforeEach(() => { - journal = mock(); - context = new AvmContext(journal); +// import { AztecAddress, Fr } from '@aztec/circuits.js'; +// import { initContext } from './fixtures/index.js'; + +describe('Avm Context', () => { + it('New call should fork context correctly', () => { + // const context = initContext(); + // const newAddress = AztecAddress.random(); + // const newCalldata = [new Fr(1), new Fr(2)]; + // const newContext = context.createNestedContractCallContext(newAddress, newCalldata); }); - it('Should execute a series of instructions', async () => { - const calldata: Fr[] = [new Fr(1), new Fr(2)]; - - const instructions: Instruction[] = [ - new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), - new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), - new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), - ]; - - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - // Set instructions (skip bytecode decoding) - context.setInstructions(instructions); - const results = await context.execute(); - - expect(results.reverted).toBe(false); - expect(results.revertReason).toBeUndefined(); - expect(results.output).toEqual([new Fr(3)]); - }); - - it('Should revert with an invalid jump', async () => { - const calldata: Fr[] = []; - - const invalidJumpDestination = 22; - - const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; - - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); - // Set instructions (skip bytecode decoding) - context.setInstructions(instructions); - const results = await context.execute(); - - expect(results.reverted).toBe(true); - expect(results.revertReason).toBeInstanceOf(InvalidProgramCounterError); - expect(results.output).toHaveLength(0); + it('New static call should fork context correctly', () => { + // const context = initContext(); + // const newAddress = AztecAddress.random(); + // const newCalldata = [new Fr(1), new Fr(2)]; + // const newContext = context.createNestedContractStaticCallContext(newAddress, newCalldata); }); }); diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index cc15e1c4b66..851f4bfc536 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -6,55 +6,58 @@ import { AvmMachineState } from './avm_machine_state.js'; import { AvmWorldStateJournal } from './journal/journal.js'; /** - * Avm Context manages the state and execution of the AVM + * An execution context includes the information necessary to initiate AVM + * execution along with all state maintained by the AVM throughout execution. */ export class AvmContext { - /** Contains constant variables provided by the kernel */ - public environment: AvmExecutionEnvironment; - /** VM state that is modified on an instruction-by-instruction basis */ - public machineState: AvmMachineState; - /** Manages mutable state during execution - (caching, fetching) */ - public worldState: AvmWorldStateJournal; - - constructor(environment: AvmExecutionEnvironment, machineState: AvmMachineState, worldState: AvmWorldStateJournal) { - this.environment = environment; - this.machineState = machineState; - this.worldState = worldState; - } + /** + * Create a new AVM context + * @param worldState - Manages mutable state during execution - (caching, fetching) + * @param environment - Contains constant variables provided by the kernel + * @param machineState - VM state that is modified on an instruction-by-instruction basis + * @returns new AvmContext instance + */ + constructor( + public worldState: AvmWorldStateJournal, + public environment: AvmExecutionEnvironment, + public machineState: AvmMachineState, + ) {} /** * Prepare a new AVM context that will be ready for an external/nested call * - Fork the world state journal + * - Derive a machine state from the current state + * - E.g., gas metering is preserved but pc is reset * - Derive an execution environment from the caller/parent - * - Alter both address and storageAddress + * - Alter both address and storageAddress * * @param address - The contract instance to initialize a context for * @param calldata - Data/arguments for nested call - * @param initialMachineState - The initial machine state (derived from call instruction args) * @returns new AvmContext instance */ public createNestedContractCallContext(address: AztecAddress, calldata: Fr[]): AvmContext { const newExecutionEnvironment = this.environment.deriveEnvironmentForNestedCall(address, calldata); - const forkedState = this.worldState.fork(); + const forkedWorldState = this.worldState.fork(); const machineState = AvmMachineState.fromState(this.machineState); - return new AvmContext(newExecutionEnvironment, machineState, forkedState); + return new AvmContext(forkedWorldState, newExecutionEnvironment, machineState); } /** * Prepare a new AVM context that will be ready for an external/nested static call * - Fork the world state journal + * - Derive a machine state from the current state + * - E.g., gas metering is preserved but pc is reset * - Derive an execution environment from the caller/parent - * - Alter both address and storageAddress + * - Alter both address and storageAddress * * @param address - The contract instance to initialize a context for * @param calldata - Data/arguments for nested call - * @param initialMachineState - The initial machine state (derived from call instruction args) * @returns new AvmContext instance */ public createNestedContractStaticCallContext(address: AztecAddress, calldata: Fr[]): AvmContext { const newExecutionEnvironment = this.environment.deriveEnvironmentForNestedStaticCall(address, calldata); - const forkedState = this.worldState.fork(); + const forkedWorldState = this.worldState.fork(); const machineState = AvmMachineState.fromState(this.machineState); - return new AvmContext(newExecutionEnvironment, machineState, forkedState); + return new AvmContext(forkedWorldState, newExecutionEnvironment, machineState); } } diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 28bef8c22d9..6d4dd4e8cb1 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -52,10 +52,6 @@ export class AvmMachineState { return new AvmMachineState(state.l1GasLeft, state.l2GasLeft, state.daGasLeft); } - public static ZERO() { - return new AvmMachineState(/*l1GasLeft=*/ 0, /*l2GasLeft=*/ 0, /*daGasLeft=*/ 0); - } - /** * Most instructions just increment PC before they complete */ diff --git a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts index 35d9099b575..62a2c1ab56d 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.test.ts @@ -2,32 +2,14 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { jest } from '@jest/globals'; -import { MockProxy, mock } from 'jest-mock-extended'; -import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../index.js'; -import { AvmContext } from './avm_context.js'; -import { AvmMachineState } from './avm_machine_state.js'; import { TypeTag } from './avm_memory_types.js'; import { AvmSimulator } from './avm_simulator.js'; -import { initExecutionEnvironment } from './fixtures/index.js'; -import { HostStorage } from './journal/host_storage.js'; -import { AvmWorldStateJournal } from './journal/journal.js'; +import { initContext, initExecutionEnvironment } from './fixtures/index.js'; import { Add, CalldataCopy, Return } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; describe('avm', () => { - let journal: AvmWorldStateJournal; - let contractsDb: MockProxy; - - beforeEach(() => { - contractsDb = mock(); - - const commitmentsDb = mock(); - const publicStateDb = mock(); - const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); - journal = new AvmWorldStateJournal(hostStorage); - }); - it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; @@ -38,9 +20,9 @@ describe('avm', () => { new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), ]); - jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); + const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + jest.spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); - const context = new AvmContext(initExecutionEnvironment({ calldata }), AvmMachineState.ZERO(), journal); const results = await new AvmSimulator(context).execute(); expect(results.reverted).toBe(false); @@ -61,9 +43,9 @@ describe('avm', () => { // Decode bytecode into instructions const bytecode = Buffer.from(addArtifact.bytecode, 'base64'); - jest.spyOn(journal.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); + const context = initContext({ env: initExecutionEnvironment({ calldata }) }); + jest.spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode').mockReturnValue(Promise.resolve(bytecode)); - const context = new AvmContext(initExecutionEnvironment({ calldata }), AvmMachineState.ZERO(), journal); const results = await new AvmSimulator(context).execute(); expect(results.reverted).toBe(false); diff --git a/yarn-project/acir-simulator/src/avm/avm_simulator.ts b/yarn-project/acir-simulator/src/avm/avm_simulator.ts index 842b86e62c5..a3c35012d00 100644 --- a/yarn-project/acir-simulator/src/avm/avm_simulator.ts +++ b/yarn-project/acir-simulator/src/avm/avm_simulator.ts @@ -1,6 +1,5 @@ import { FunctionSelector } from '@aztec/circuits.js'; -// import { Fr } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { strict as assert } from 'assert'; @@ -11,21 +10,25 @@ import type { Instruction } from './opcodes/index.js'; import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; export class AvmSimulator { - private log = createDebugLogger('todo-here'); + private log: DebugLogger = createDebugLogger('aztec:avm_simulator'); constructor(private context: AvmContext) {} /** - * Execute the contract code within the current context. + * Fetch the bytecode and execute it in the current context. */ public async execute(): Promise { const instructions = await this.fetchAndDecodeBytecode(); - // Cannot execute empty contract or uninitialized context - assert(instructions.length > 0); return this.executeInstructions(instructions); } + /** + * Executes the provided instructions in the current context. + * This method is useful for testing and debugging. + */ public async executeInstructions(instructions: Instruction[]): Promise { + assert(instructions.length > 0); + try { // Execute instruction pointed to by the current program counter // continuing until the machine state signifies a halt diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index 689f17241b3..bdbf5bed4bc 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -1,11 +1,37 @@ -// Place large AVM text fixtures in here import { GlobalVariables } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { mock } from 'jest-mock-extended'; + +import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; +import { AvmContext } from '../avm_context.js'; import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; -import { AvmMachineState, InitialAvmMachineState } from '../avm_machine_state.js'; +import { AvmMachineState } from '../avm_machine_state.js'; +import { HostStorage } from '../journal/host_storage.js'; +import { AvmWorldStateJournal } from '../journal/journal.js'; + +/** + * Create a new AVM context with default values. + */ +export function initContext(overrides?: { + worldState?: AvmWorldStateJournal; + env?: AvmExecutionEnvironment; + machineState?: AvmMachineState; +}): AvmContext { + return new AvmContext( + overrides?.worldState || initMockWorldStateJournal(), + overrides?.env || initExecutionEnvironment(), + overrides?.machineState || initMachineState(), + ); +} + +/** Creates an empty world state with mocked storage. */ +export function initMockWorldStateJournal(): AvmWorldStateJournal { + const hostStorage = new HostStorage(mock(), mock(), mock()); + return new AvmWorldStateJournal(hostStorage); +} /** * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object @@ -40,22 +66,11 @@ export function initGlobalVariables(overrides?: Partial): Globa ); } -/** - * Create an empty instance of the "Initial" Machine State where all values are zero, unless overridden in the overrides object - */ -export function initInitialMachineState(overrides?: Partial): InitialAvmMachineState { - return { - l1GasLeft: overrides?.l1GasLeft ?? 0, - l2GasLeft: overrides?.l2GasLeft ?? 0, - daGasLeft: overrides?.daGasLeft ?? 0, - }; -} - /** * Create an empty instance of the Machine State where all values are zero, unless overridden in the overrides object */ export function initMachineState(overrides?: Partial): AvmMachineState { - return new AvmMachineState({ + return AvmMachineState.fromState({ l1GasLeft: overrides?.l1GasLeft ?? 0, l2GasLeft: overrides?.l2GasLeft ?? 0, daGasLeft: overrides?.daGasLeft ?? 0, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index 6de98453044..83996e1e3b7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -1,21 +1,14 @@ -import { mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; -import { HostStorage } from '../journal/host_storage.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext, initExecutionEnvironment } from '../fixtures/index.js'; import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; import { StaticCallStorageAlterError } from './storage.js'; describe('Accrued Substate', () => { let context: AvmContext; - let journal: AvmWorldStateJournal; beforeEach(() => { - const hostStorage = mock(); - journal = new AvmWorldStateJournal(hostStorage); - context = new AvmContext(journal); + context = initContext(); }); describe('EmitNoteHash', () => { @@ -129,7 +122,7 @@ describe('Accrued Substate', () => { }); it('All substate instructions should fail within a static call', async () => { - context = new AvmContext(journal, initExecutionEnvironment({ isStaticCall: true })); + context = initContext({ env: initExecutionEnvironment({ isStaticCall: true }) }); const instructions = [ new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0), diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 7758d7f0322..a66201a9808 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,20 +1,13 @@ -import { mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; -import { AvmMachineState } from '../avm_machine_state.js'; import { Field, TypeTag } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext } from '../fixtures/index.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { let context: AvmContext; beforeEach(() => { - const journal = mock(); - const machineState = AvmMachineState.ZERO(); - const env = initExecutionEnvironment(); - context = new AvmContext(env, machineState, journal); + context = initContext(); }); describe('Add', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 59291479683..8a16f215f03 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -1,17 +1,13 @@ -import { MockProxy, mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext } from '../fixtures/index.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; describe('Bitwise instructions', () => { let context: AvmContext; - let journal: MockProxy; - beforeEach(async () => { - journal = mock(); - context = new AvmContext(journal); + beforeEach(() => { + context = initContext(); }); describe('AND', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index 0fb74b4bb51..c8001c7759f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -1,18 +1,14 @@ -import { MockProxy, mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; import { TagCheckError } from '../errors.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext } from '../fixtures/index.js'; import { Eq, Lt, Lte } from './comparators.js'; describe('Comparators', () => { let context: AvmContext; - let journal: MockProxy; - beforeEach(async () => { - journal = mock(); - context = new AvmContext(journal); + beforeEach(() => { + context = initContext(); }); describe('Eq', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 47c31b24c6c..fee81ec3402 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -1,20 +1,16 @@ import { Fr } from '@aztec/foundation/fields'; -import { MockProxy, mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; import { Field, Uint16 } from '../avm_memory_types.js'; import { InstructionExecutionError } from '../errors.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext } from '../fixtures/index.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; describe('Control Flow Opcodes', () => { let context: AvmContext; - let journal: MockProxy; beforeEach(() => { - journal = mock(); - context = new AvmContext(journal); + context = initContext(); }); describe('JUMP', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index fc9ebbdc5aa..0af20744a53 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -1,10 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; -import { MockProxy, mock } from 'jest-mock-extended'; - -import { AvmContext } from '../avm_context.js'; -import { initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext, initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; import { Address, BlockNumber, @@ -21,16 +17,9 @@ import { } from './environment_getters.js'; describe('Environment getters instructions', () => { - let context: AvmContext; - let journal: MockProxy; - - beforeEach(() => { - journal = mock(); - }); - type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; const envGetterTest = async (key: string, value: Fr, instruction: EnvInstruction) => { - context = new AvmContext(journal, initExecutionEnvironment({ [key]: value })); + const context = initContext({ env: initExecutionEnvironment({ [key]: value }) }); await instruction.execute(context); const actual = context.machineState.memory.get(0).toFr(); @@ -193,7 +182,7 @@ describe('Environment getters instructions', () => { type GlobalsInstruction = ChainId | Version | BlockNumber | Timestamp; const readGlobalVariableTest = async (key: string, value: Fr, instruction: GlobalsInstruction) => { const globals = initGlobalVariables({ [key]: value }); - context = new AvmContext(journal, initExecutionEnvironment({ globals })); + const context = initContext({ env: initExecutionEnvironment({ globals }) }); await instruction.execute(context); const actual = context.machineState.memory.get(0).toFr(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index d7285fcbe17..a7b0bab2e69 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -1,11 +1,12 @@ import { Fr } from '@aztec/foundation/fields'; import { jest } from '@jest/globals'; -import { MockProxy, mock } from 'jest-mock-extended'; +import { mock } from 'jest-mock-extended'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; +import { initContext } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; @@ -17,18 +18,14 @@ import { SStore } from './storage.js'; describe('External Calls', () => { let context: AvmContext; - let journal: AvmWorldStateJournal; - - let contractsDb: MockProxy; beforeEach(() => { - contractsDb = mock(); - + const contractsDb = mock(); const commitmentsDb = mock(); const publicStateDb = mock(); const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); - journal = new AvmWorldStateJournal(hostStorage); - context = new AvmContext(journal); + const journal = new AvmWorldStateJournal(hostStorage); + context = initContext({ worldState: journal }); }); describe('Call', () => { @@ -81,7 +78,7 @@ describe('External Calls', () => { context.machineState.memory.set(1, new Field(addr)); context.machineState.memory.setSlice(2, args); jest - .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); const instruction = new Call( @@ -103,7 +100,7 @@ describe('External Calls', () => { expect(retValue).toEqual([new Field(1n), new Field(2n)]); // Check that the storage call has been merged into the parent journal - const { currentStorageValue } = journal.flush(); + const { currentStorageValue } = context.worldState.flush(); expect(currentStorageValue.size).toEqual(1); const nestedContractWrites = currentStorageValue.get(addr.toBigInt()); @@ -168,7 +165,7 @@ describe('External Calls', () => { const otherContextInstructionsBytecode = encodeToBytecode(otherContextInstructions); jest - .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .spyOn(context.worldState.hostStorage.contractsDb, 'getBytecode') .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); const instruction = new StaticCall( diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 9ff147346f8..4889bca0a99 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -1,21 +1,16 @@ import { Fr } from '@aztec/foundation/fields'; -import { MockProxy, mock } from 'jest-mock-extended'; - import { AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { InstructionExecutionError } from '../errors.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; -import { AvmWorldStateJournal } from '../journal/journal.js'; +import { initContext, initExecutionEnvironment } from '../fixtures/index.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Memory instructions', () => { let context: AvmContext; - let journal: MockProxy; - beforeEach(async () => { - journal = mock(); - context = new AvmContext(journal); + beforeEach(() => { + context = initContext(); }); describe('SET', () => { @@ -360,7 +355,7 @@ describe('Memory instructions', () => { it('Writes nothing if size is 0', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); + context = initContext({ env: initExecutionEnvironment({ calldata }) }); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(context); @@ -371,7 +366,7 @@ describe('Memory instructions', () => { it('Copies all calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); + context = initContext({ env: initExecutionEnvironment({ calldata }) }); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(context); @@ -382,7 +377,7 @@ describe('Memory instructions', () => { it('Copies slice of calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - context = new AvmContext(journal, initExecutionEnvironment({ calldata })); + context = initContext({ env: initExecutionEnvironment({ calldata }) }); context.machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(context); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 830cf5486c0..dde14ba639c 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -5,7 +5,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; -import { initExecutionEnvironment } from '../fixtures/index.js'; +import { initContext, initExecutionEnvironment } from '../fixtures/index.js'; import { AvmWorldStateJournal } from '../journal/journal.js'; import { SLoad, SStore, StaticCallStorageAlterError } from './storage.js'; @@ -16,7 +16,7 @@ describe('Storage Instructions', () => { beforeEach(async () => { journal = mock(); - context = new AvmContext(journal, initExecutionEnvironment({ address, storageAddress: address })); + context = initContext({ worldState: journal, env: initExecutionEnvironment({ address, storageAddress: address }) }); }); describe('SSTORE', () => { @@ -46,7 +46,10 @@ describe('Storage Instructions', () => { }); it('Should not be able to write to storage in a static call', async () => { - context = new AvmContext(journal, initExecutionEnvironment({ isStaticCall: true })); + context = initContext({ + worldState: journal, + env: initExecutionEnvironment({ address, storageAddress: address, isStaticCall: true }), + }); const a = new Field(1n); const b = new Field(2n); diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts index adca61ef609..26e75171b65 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { Add, Address, Sub } from '../opcodes/index.js'; +import { Add, Address, Call, StaticCall, Sub } from '../opcodes/index.js'; import { BufferCursor } from './buffer_cursor.js'; import { InstructionSet, decodeFromBytecode, encodeToBytecode } from './bytecode_serialization.js'; import { Opcode } from './instruction_serialization.js'; @@ -75,6 +75,26 @@ describe('Bytecode Serialization', () => { new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Address(/*indirect=*/ 0, /*dstOffset=*/ 1), + new Call( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), + new StaticCall( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), ]; const bytecode = Buffer.concat(instructions.map(i => i.serialize())); @@ -88,6 +108,26 @@ describe('Bytecode Serialization', () => { new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Address(/*indirect=*/ 0, /*dstOffset=*/ 1), + new Call( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), + new StaticCall( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), ]; const actual = encodeToBytecode(instructions); diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index 3690393c8d8..1033cb5f785 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -3,7 +3,8 @@ import { Address, And, BlockNumber, - CMov, //Call, + CMov, + Call, CalldataCopy, Cast, ChainId, @@ -35,14 +36,15 @@ import { Sender, Set, Shl, - Shr, //StaticCall, + Shr, + StaticCall, StorageAddress, Sub, Timestamp, Version, Xor, } from '../opcodes/index.js'; -import { Instruction } from '../opcodes/instruction.js'; +import type { Instruction } from '../opcodes/index.js'; import { BufferCursor } from './buffer_cursor.js'; import { Opcode } from './instruction_serialization.js'; @@ -52,8 +54,10 @@ interface DeserializableInstruction { } export type InstructionSet = Map; -const INSTRUCTION_SET: InstructionSet = new 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. +const INSTRUCTION_SET = () => + new Map([ [Add.opcode, Add], [Sub.opcode, Sub], [Mul.opcode, Mul], @@ -116,16 +120,15 @@ const INSTRUCTION_SET: InstructionSet = new Map