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

chore(avm): refactor AVM Simulator and fix issues #4424

Merged
merged 17 commits into from
Feb 5, 2024
Prev Previous commit
Next Next commit
cleanup
dbanks12 committed Feb 4, 2024
commit 67e4fdc3545890d2c39c531958cb27b0536acccf
44 changes: 26 additions & 18 deletions yarn-project/acir-simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
@@ -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<AvmContractCallResults> {
async execute(skipBytecodeFetch: boolean): Promise<AvmContractCallResults> {
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;
}
}
11 changes: 8 additions & 3 deletions yarn-project/acir-simulator/src/avm/avm_machine_state.ts
Original file line number Diff line number Diff line change
@@ -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[] = [];

2 changes: 1 addition & 1 deletion yarn-project/acir-simulator/src/avm/errors.ts
Original file line number Diff line number Diff line change
@@ -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';
4 changes: 2 additions & 2 deletions yarn-project/acir-simulator/src/avm/index.test.ts
Original file line number Diff line number Diff line change
@@ -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();