Skip to content

Commit

Permalink
feat(avm): link up storage (AztecProtocol#4150)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maddiaa0 authored Jan 25, 2024
1 parent e5ff2f6 commit 3e86870
Show file tree
Hide file tree
Showing 16 changed files with 274 additions and 37 deletions.
17 changes: 9 additions & 8 deletions yarn-project/acir-simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
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 { AvmStateManager } from './avm_state_manager.js';
Expand All @@ -13,9 +12,13 @@ import { Instruction } from './opcodes/index.js';
* It stores a state manager
*/
export class AvmContext {
/** Contains constant variables provided by the kernel */
private executionEnvironment: AvmExecutionEnvironment;
/** A wrapper that manages mutable state during execution - (caching, fetching) */
private stateManager: AvmStateManager;

constructor(stateManager: AvmStateManager) {
constructor(executionEnvironment: AvmExecutionEnvironment, stateManager: AvmStateManager) {
this.executionEnvironment = executionEnvironment;
this.stateManager = stateManager;
}

Expand All @@ -26,17 +29,15 @@ export class AvmContext {
* - We interpret the bytecode
* - We run the interpreter
*
* @param contractAddress -
* @param calldata -
*/
public call(contractAddress: Fr, calldata: Fr[]): AvmMessageCallResult {
public call(): AvmMessageCallResult {
// NOTE: the following is mocked as getPublicBytecode does not exist yet
// const bytecode = stateManager.journal.hostStorage.contractsDb.getBytecode(contractAddress);
// const bytecode = stateManager.journal.hostStorage.contractsDb.getBytecode(this.executionEnvironment.address);
const bytecode = Buffer.from('0x01000100020003');

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

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

return interpreter.run();
Expand Down
39 changes: 39 additions & 0 deletions yarn-project/acir-simulator/src/avm/avm_execution_environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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';

/**
* Contains variables that remain constant during AVM execution
* These variables are provided by the public kernel circuit
*/
export class AvmExecutionEnvironment {
constructor(
/** - */
public readonly address: AztecAddress,
/** - */
public readonly storageAddress: AztecAddress,
/** - */
public readonly origin: AztecAddress,
/** - */
public readonly sender: AztecAddress,
/** - */
public readonly portal: EthAddress,
/** - */
public readonly feePerL1Gas: Fr,
/** - */
public readonly feePerL2Gas: Fr,
/** - */
public readonly feePerDaGas: Fr,
/** - */
public readonly contractCallDepth: Fr,
/** - */
public readonly globals: GlobalVariables,
/** - */
public readonly isStaticCall: boolean,
/** - */
public readonly isDelegateCall: boolean,
/** - */
public readonly calldata: Fr[],
) {}
}
16 changes: 11 additions & 5 deletions yarn-project/acir-simulator/src/avm/avm_machine_state.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Fr } from '@aztec/foundation/fields';

import { AvmExecutionEnvironment } from './avm_execution_environment.js';
import { TaggedMemory } from './avm_memory_types.js';

/**
* Store's data for an Avm execution frame
*/
export class AvmMachineState {
/** - */
public readonly calldata: Fr[];
/**
* 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[];

/** - */
Expand All @@ -31,10 +36,9 @@ export class AvmMachineState {

/**
* Create a new avm context
* @param calldata -
* @param executionEnvironment - Machine context that is passed to the avm
*/
constructor(calldata: Fr[]) {
this.calldata = calldata;
constructor(executionEnvironment: AvmExecutionEnvironment) {
this.returnData = [];
this.memory = new TaggedMemory();
this.internalCallStack = [];
Expand All @@ -43,6 +47,8 @@ export class AvmMachineState {
this.callStack = [];

this.halted = false;

this.executionEnvironment = executionEnvironment;
}

/**
Expand Down
22 changes: 21 additions & 1 deletion yarn-project/acir-simulator/src/avm/avm_state_manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BlockHeader } from '@aztec/circuits.js';
import { AztecAddress, BlockHeader } from '@aztec/circuits.js';
import { Fr } from '@aztec/foundation/fields';

import { AvmJournal, HostStorage } from './journal/index.js';

Expand Down Expand Up @@ -42,4 +43,23 @@ export class AvmStateManager {
const journal = AvmJournal.branchParent(parent.journal);
return new AvmStateManager(parent.blockHeader, journal);
}

/**
* Passes storage call to the journal
* @param contractAddress -
* @param slot -
* @param value -
*/
public store(contractAddress: AztecAddress, slot: Fr, value: Fr): void {
this.journal.writeStorage(contractAddress, slot, value);
}

/**
* Passes storage read from the journal
* @param contractAddress -
* @param slot -
*/
public read(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
return this.journal.readStorage(contractAddress, slot);
}
}
59 changes: 59 additions & 0 deletions yarn-project/acir-simulator/src/avm/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
// 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 { AvmExecutionEnvironment } from '../avm_execution_environment.js';

/**
* An interface that allows to override the default values of the AvmExecutionEnvironment
*/
export interface AvmExecutionEnvironmentOverrides {
/** - */
address?: AztecAddress;
/** - */
storageAddress?: AztecAddress;
/** - */
origin?: AztecAddress;
/** - */
sender?: AztecAddress;
/** - */
portal?: EthAddress;
/** - */
feePerL1Gas?: Fr;
/** - */
feePerL2Gas?: Fr;
/** - */
feePerDaGas?: Fr;
/** - */
contractCallDepth?: Fr;
/** - */
globals?: GlobalVariables;
/** - */
isStaticCall?: boolean;
/** - */
isDelegateCall?: boolean;
/** - */
calldata?: Fr[];
}

/**
* Create an empty instance of the Execution Environment where all values are zero, unless overriden in the overrides object
*/
export function initExecutionEnvironment(overrides?: AvmExecutionEnvironmentOverrides): AvmExecutionEnvironment {
return new AvmExecutionEnvironment(
overrides?.address ?? AztecAddress.zero(),
overrides?.storageAddress ?? AztecAddress.zero(),
overrides?.origin ?? AztecAddress.zero(),
overrides?.sender ?? AztecAddress.zero(),
overrides?.portal ?? EthAddress.ZERO,
overrides?.feePerL1Gas ?? Fr.zero(),
overrides?.feePerL2Gas ?? Fr.zero(),
overrides?.feePerDaGas ?? Fr.zero(),
overrides?.contractCallDepth ?? Fr.zero(),
overrides?.globals ?? GlobalVariables.empty(),
overrides?.isStaticCall ?? false,
overrides?.isDelegateCall ?? false,
overrides?.calldata ?? [],
);
}
3 changes: 2 additions & 1 deletion yarn-project/acir-simulator/src/avm/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { mock } from 'jest-mock-extended';

import { AvmMachineState } from './avm_machine_state.js';
import { AvmStateManager } from './avm_state_manager.js';
import { initExecutionEnvironment } from './fixtures/index.js';
import { AvmInterpreter } from './interpreter/interpreter.js';
import { decodeBytecode } from './opcodes/decode_bytecode.js';
import { encodeToBytecode } from './opcodes/encode_to_bytecode.js';
Expand All @@ -28,7 +29,7 @@ describe('avm', () => {
const instructions = decodeBytecode(fullBytecode);

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { Fr } from '@aztec/foundation/fields';

import { mock } from 'jest-mock-extended';
import { MockProxy, mock } from 'jest-mock-extended';

import { AvmMachineState } from '../avm_machine_state.js';
import { AvmStateManager } from '../avm_state_manager.js';
import { initExecutionEnvironment } from '../fixtures/index.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 { AvmInterpreter, InvalidProgramCounterError } from './interpreter.js';

describe('interpreter', () => {
let stateManager: MockProxy<AvmStateManager>;

beforeEach(() => {
stateManager = mock<AvmStateManager>();
});

it('Should execute a series of instructions', () => {
const calldata: Fr[] = [new Fr(1), new Fr(2)];
const stateManager = mock<AvmStateManager>();

const instructions: Instruction[] = [
new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 2, /*destOffset=*/ 0),
new Add(/*aOffset=*/ 0, /*bOffset=*/ 1, /*destOffset=*/ 2),
new Return(/*returnOffset=*/ 2, /*copySize=*/ 1),
];

const context = new AvmMachineState(calldata);
const context = new AvmMachineState(initExecutionEnvironment({ calldata }));
const interpreter = new AvmInterpreter(context, stateManager, instructions);
const avmReturnData = interpreter.run();

Expand All @@ -32,13 +38,12 @@ describe('interpreter', () => {

it('Should revert with an invalid jump', () => {
const calldata: Fr[] = [];
const stateManager = mock<AvmStateManager>();

const invalidJumpDestination = 22;

const instructions: Instruction[] = [new Jump(invalidJumpDestination)];

const context = new AvmMachineState(calldata);
const context = new AvmMachineState(initExecutionEnvironment({ calldata }));
const interpreter = new AvmInterpreter(context, stateManager, instructions);

const avmReturnData = interpreter.run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { mock } from 'jest-mock-extended';
import { MockProxy, mock } from 'jest-mock-extended';

import { AvmMachineState } from '../avm_machine_state.js';
import { Field } from '../avm_memory_types.js';
import { AvmStateManager } from '../avm_state_manager.js';
import { initExecutionEnvironment } from '../fixtures/index.js';
import { Add, Div, Mul, Sub } from './arithmetic.js';

describe('Arithmetic Instructions', () => {
let machineState: AvmMachineState;
let stateManager = mock<AvmStateManager>();
let stateManager: MockProxy<AvmStateManager>;

beforeEach(() => {
machineState = new AvmMachineState([]);
machineState = new AvmMachineState(initExecutionEnvironment());
stateManager = mock<AvmStateManager>();
});

Expand Down
3 changes: 2 additions & 1 deletion yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { mock } from 'jest-mock-extended';
import { AvmMachineState } from '../avm_machine_state.js';
import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js';
import { AvmStateManager } from '../avm_state_manager.js';
import { initExecutionEnvironment } from '../fixtures/index.js';
import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js';

describe('Bitwise instructions', () => {
let machineState: AvmMachineState;
let stateManager = mock<AvmStateManager>();

beforeEach(() => {
machineState = new AvmMachineState([]);
machineState = new AvmMachineState(initExecutionEnvironment());
stateManager = mock<AvmStateManager>();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { mock } from 'jest-mock-extended';
import { MockProxy, mock } from 'jest-mock-extended';

import { AvmMachineState } from '../avm_machine_state.js';
import { TypeTag, Uint16 } from '../avm_memory_types.js';
import { AvmStateManager } from '../avm_state_manager.js';
import { initExecutionEnvironment } from '../fixtures/index.js';
import { Add, Mul, Sub } from './arithmetic.js';
import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js';
import { Eq, Lt, Lte } from './comparators.js';
Expand All @@ -11,12 +12,12 @@ import { InstructionExecutionError } from './instruction.js';
import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js';

describe('Control Flow Opcodes', () => {
let stateManager = mock<AvmStateManager>();
let stateManager: MockProxy<AvmStateManager>;
let machineState: AvmMachineState;

beforeEach(() => {
stateManager = mock<AvmStateManager>();
machineState = new AvmMachineState([]);
machineState = new AvmMachineState(initExecutionEnvironment());
});

it('Should implement JUMP', () => {
Expand Down Expand Up @@ -138,7 +139,7 @@ describe('Control Flow Opcodes', () => {

for (const instruction of instructions) {
// Use a fresh machine state each run
const innerMachineState = new AvmMachineState([]);
const innerMachineState = new AvmMachineState(initExecutionEnvironment());
innerMachineState.memory.set(0, new Uint16(4n));
innerMachineState.memory.set(1, new Uint16(8n));
innerMachineState.memory.set(2, new Uint16(12n));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Add, Div, Mul, Sub } from './arithmetic.js';
import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js';
//import { Eq, Lt, Lte } from './comparators.js';
import { InternalCall, InternalReturn, Jump, JumpI, Return } from './control_flow.js';
import { Instruction } from './instruction.js';
import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js';
import { Opcode } from './opcodes.js';
//import { Eq, Lt, Lte } from './comparators.js';
import { SLoad, SStore } from './storage.js';

/** - */
type InstructionConstructor = new (...args: any[]) => Instruction;
Expand Down Expand Up @@ -75,8 +76,8 @@ export const INSTRUCTION_SET: Map<Opcode, InstructionConstructorAndMembers> = ne

//// World State
//[Opcode.BLOCKHEADERBYNUMBER, Blockheaderbynumber],
//[Opcode.SLOAD, Sload], // Public Storage
//[Opcode.SSTORE, Sstore], // Public Storage
[Opcode.SLOAD, SLoad], // Public Storage
[Opcode.SSTORE, SStore], // Public Storage
//[Opcode.READL1TOL2MSG, Readl1tol2msg], // Messages
//[Opcode.SENDL2TOL1MSG, Sendl2tol1msg], // Messages
//[Opcode.EMITNOTEHASH, Emitnotehash], // Notes & Nullifiers
Expand Down
Loading

0 comments on commit 3e86870

Please sign in to comment.