Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(avm): make interpreter a function not a class #4272

Merged
merged 22 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions yarn-project/acir-simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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 { AvmInterpreter, AvmInterpreterError } from './interpreter/index.js';
import { AvmInterpreterError, executeAvm } from './interpreter/index.js';
import { AvmJournal } from './journal/journal.js';
import { decodeBytecode } from './opcodes/decode_bytecode.js';
import { Instruction } from './opcodes/index.js';
Expand Down Expand Up @@ -49,10 +49,8 @@ export class AvmContext {

const instructions: Instruction[] = decodeBytecode(bytecode);

const context = new AvmMachineState(this.executionEnvironment);
const interpreter = new AvmInterpreter(context, this.journal, instructions);

return interpreter.run();
const machineState = new AvmMachineState(this.executionEnvironment);
return executeAvm(machineState, this.journal, instructions);
}

/**
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/acir-simulator/src/avm/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { mock } from 'jest-mock-extended';

import { AvmMachineState } from './avm_machine_state.js';
import { initExecutionEnvironment } from './fixtures/index.js';
import { AvmInterpreter } from './interpreter/interpreter.js';
import { executeAvm } from './interpreter/interpreter.js';
import { AvmJournal } from './journal/journal.js';
import { decodeBytecode } from './opcodes/decode_bytecode.js';
import { encodeToBytecode } from './opcodes/encode_to_bytecode.js';
Expand All @@ -30,8 +30,7 @@ describe('avm', () => {

// Execute instructions
const context = new AvmMachineState(initExecutionEnvironment({ calldata }));
const interpreter = new AvmInterpreter(context, journal, instructions);
const avmReturnData = await interpreter.run();
const avmReturnData = await executeAvm(context, journal, instructions);

expect(avmReturnData.reverted).toBe(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ 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 { AvmInterpreter, InvalidProgramCounterError } from './interpreter.js';
import { InvalidProgramCounterError, executeAvm } from './interpreter.js';

describe('interpreter', () => {
let journal: MockProxy<AvmJournal>;
Expand All @@ -28,8 +28,7 @@ describe('interpreter', () => {
];

const machineState = new AvmMachineState(initExecutionEnvironment({ calldata }));
const interpreter = new AvmInterpreter(machineState, journal, instructions);
const avmReturnData = await interpreter.run();
const avmReturnData = await executeAvm(machineState, journal, instructions);

expect(avmReturnData.reverted).toBe(false);
expect(avmReturnData.revertReason).toBeUndefined();
Expand All @@ -44,9 +43,7 @@ describe('interpreter', () => {
const instructions: Instruction[] = [new Jump(invalidJumpDestination)];

const machineState = new AvmMachineState(initExecutionEnvironment({ calldata }));
const interpreter = new AvmInterpreter(machineState, journal, instructions);

const avmReturnData = await interpreter.run();
const avmReturnData = await executeAvm(machineState, journal, instructions);

expect(avmReturnData.reverted).toBe(true);
expect(avmReturnData.revertReason).toBeInstanceOf(InvalidProgramCounterError);
Expand Down
82 changes: 29 additions & 53 deletions yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Fr } from '@aztec/foundation/fields';

import { strict as assert } from 'assert';

import { AvmMachineState } from '../avm_machine_state.js';
Expand All @@ -8,66 +6,44 @@ import { AvmJournal } from '../journal/index.js';
import { Instruction } from '../opcodes/index.js';

/**
* Avm Interpreter
*
* Executes an Avm context
* Run the avm
* @returns bool - successful execution will return true
* - reverted execution will return false
* - any other panic will throw
*/
export class AvmInterpreter {
private instructions: Instruction[] = [];
private machineState: AvmMachineState;
private journal: AvmJournal;

constructor(machineState: AvmMachineState, stateManager: AvmJournal, instructions: Instruction[]) {
this.machineState = machineState;
this.journal = stateManager;
this.instructions = instructions;
}

/**
* Run the avm
* @returns bool - successful execution will return true
* - reverted execution will return false
* - any other panic will throw
*/
async run(): Promise<AvmMessageCallResult> {
assert(this.instructions.length > 0);

try {
while (!this.machineState.halted) {
const instruction = this.instructions[this.machineState.pc];
assert(!!instruction); // This should never happen
export async function executeAvm(
machineState: AvmMachineState,
journal: AvmJournal,
instructions: Instruction[] = [],
): Promise<AvmMessageCallResult> {
assert(instructions.length > 0);

await instruction.execute(this.machineState, this.journal);
try {
while (!machineState.halted) {
const instruction = instructions[machineState.pc];
assert(!!instruction); // This should never happen

if (this.machineState.pc >= this.instructions.length) {
throw new InvalidProgramCounterError(this.machineState.pc, /*max=*/ this.instructions.length);
}
}
await instruction.execute(machineState, journal);

const returnData = this.machineState.getReturnData();
if (this.machineState.reverted) {
return AvmMessageCallResult.revert(returnData);
if (machineState.pc >= instructions.length) {
throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
}
}

return AvmMessageCallResult.success(returnData);
} catch (_e) {
if (!(_e instanceof AvmInterpreterError)) {
throw _e;
}
const returnData = machineState.getReturnData();
if (machineState.reverted) {
return AvmMessageCallResult.revert(returnData);
}

const revertReason: AvmInterpreterError = _e;
const revertData = this.machineState.getReturnData();
return AvmMessageCallResult.revert(revertData, revertReason);
return AvmMessageCallResult.success(returnData);
} catch (_e) {
if (!(_e instanceof AvmInterpreterError)) {
throw _e;
}
}

/**
* Get the return data from avm execution
* TODO: this should fail if the code has not been executed
* - maybe move the return in run into a variable and track it
*/
returnData(): Fr[] {
return this.machineState.getReturnData();
const revertReason: AvmInterpreterError = _e;
const revertData = machineState.getReturnData();
return AvmMessageCallResult.revert(revertData, revertReason);
}
}

Expand Down
Loading