Skip to content

Commit

Permalink
feat(avm): link up storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Maddiaa0 committed Jan 19, 2024
1 parent b40f42c commit 17a2005
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 21 deletions.
12 changes: 7 additions & 5 deletions yarn-project/acir-simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Fr } from '@aztec/foundation/fields';

import { ExecutionEnvironment } 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 @@ -14,9 +15,11 @@ import { Instruction } from './opcodes/index.js';
*/
export class AvmContext {
private stateManager: AvmStateManager;
private executionEnvironment: ExecutionEnvironment;

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

/**
Expand All @@ -26,17 +29,16 @@ export class AvmContext {
* - We interpret the bytecode
* - We run the interpreter
*
* @param contractAddress -
* @param calldata -
*/
public call(contractAddress: Fr, calldata: Fr[]): AvmMessageCallResult {
public call(calldata: Fr[]): 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(calldata, this.executionEnvironment);
const interpreter = new AvmInterpreter(context, this.stateManager, instructions);

return interpreter.run();
Expand Down
52 changes: 52 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,52 @@
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';

/** - */
export class ExecutionEnvironment {
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,
/** - */
// globals: TODO:
/** - */
public readonly isStaticCall: boolean,
/** - */
public readonly isDelegateCall: boolean,
/** - */
public readonly calldata: Fr[],
) {}

static empty(): ExecutionEnvironment {
return new ExecutionEnvironment(
AztecAddress.zero(),
AztecAddress.zero(),
AztecAddress.zero(),
AztecAddress.zero(),
EthAddress.ZERO,
Fr.zero(),
Fr.zero(),
Fr.zero(),
Fr.zero(),
false,
false,
[],
);
}
}
13 changes: 12 additions & 1 deletion yarn-project/acir-simulator/src/avm/avm_machine_state.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { Fr } from '@aztec/foundation/fields';

import { ExecutionEnvironment } from './avm_execution_environment.js';

/**
* 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: ExecutionEnvironment;

/** - */
public readonly calldata: Fr[];
private returnData: Fr[];
Expand Down Expand Up @@ -31,8 +39,9 @@ export class AvmMachineState {
/**
* Create a new avm context
* @param calldata -
* @param executionEnvironment - Machine context that is passed to the avm
*/
constructor(calldata: Fr[]) {
constructor(calldata: Fr[], executionEnvironment: ExecutionEnvironment) {
this.calldata = calldata;
this.returnData = [];
this.memory = [];
Expand All @@ -42,6 +51,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);
}
}
39 changes: 39 additions & 0 deletions yarn-project/acir-simulator/src/avm/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,40 @@
// Place large AVM text fixtures in here
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';

import { ExecutionEnvironment } from '../avm_execution_environment.js';

export const initExecutionEnvironmentEmpty = (): ExecutionEnvironment => {
return new ExecutionEnvironment(
AztecAddress.zero(),
AztecAddress.zero(),
AztecAddress.zero(),
AztecAddress.zero(),
EthAddress.ZERO,
Fr.zero(),
Fr.zero(),
Fr.zero(),
Fr.zero(),
false,
false,
[],
);
};

export const initExecutionEnvironment = (contractAddress: AztecAddress): ExecutionEnvironment => {
return new ExecutionEnvironment(
contractAddress,
contractAddress,
AztecAddress.zero(),
AztecAddress.zero(),
EthAddress.ZERO,
Fr.zero(),
Fr.zero(),
Fr.zero(),
Fr.zero(),
false,
false,
[],
);
};
4 changes: 3 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 { initExecutionEnvironmentEmpty } 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,8 @@ describe('avm', () => {
const instructions = decodeBytecode(fullBytecode);

// Execute instructions
const context = new AvmMachineState(calldata);
const executionEnvironment = initExecutionEnvironmentEmpty();
const context = new AvmMachineState(calldata, executionEnvironment);
const interpreter = new AvmInterpreter(context, stateManager, instructions);
const avmReturnData = interpreter.run();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
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 { initExecutionEnvironmentEmpty } 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 } 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[] = [
// Copy the first two elements of the calldata to memory regions 0 and 1
Expand All @@ -24,7 +30,7 @@ describe('interpreter', () => {
new Return(2, 1), // [3]
];

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

Expand All @@ -37,13 +43,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(calldata, initExecutionEnvironmentEmpty());
const interpreter = new AvmInterpreter(context, stateManager, instructions);

const avmReturnData = interpreter.run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
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 { initExecutionEnvironmentEmpty } 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([], initExecutionEnvironmentEmpty());
stateManager = mock<AvmStateManager>();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
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 { initExecutionEnvironmentEmpty } 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';
import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js';
import { 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([], initExecutionEnvironmentEmpty());
});

it('Should implement JUMP', () => {
Expand Down Expand Up @@ -137,7 +138,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([], initExecutionEnvironmentEmpty());
expect(machineState.pc).toBe(0);
instruction.execute(innerMachineState, stateManager);
expect(innerMachineState.pc).toBe(1);
Expand Down
53 changes: 53 additions & 0 deletions yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

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 { SLoad, SStore } from './storage.js';

describe('Storage Instructions', () => {
let stateManager: MockProxy<AvmStateManager>;
let machineState: AvmMachineState;
const contractAddress = AztecAddress.random();

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

const executionEnvironment = initExecutionEnvironment(contractAddress);
machineState = new AvmMachineState([], executionEnvironment);
});

it('Sstore should Write into storage', () => {
const a = new Fr(1n);
const b = new Fr(2n);

machineState.writeMemory(0, a);
machineState.writeMemory(1, b);

new SStore(0, 1).execute(machineState, stateManager);

expect(stateManager.store).toBeCalledWith(contractAddress, a, b);
});

it('Sload should Read into storage', async () => {
// Mock response
const expectedResult = new Fr(1n);
stateManager.read.mockReturnValueOnce(Promise.resolve(expectedResult));

const a = new Fr(1n);
const b = new Fr(2n);

machineState.writeMemory(0, a);
machineState.writeMemory(1, b);

await new SLoad(0, 1).execute(machineState, stateManager);

expect(stateManager.read).toBeCalledWith(contractAddress, a);

const actual = machineState.readMemory(1);
expect(actual).toEqual(expectedResult);
});
});
Loading

0 comments on commit 17a2005

Please sign in to comment.