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

feat(avm): link up storage #4150

Merged
merged 20 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
14 changes: 9 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 { 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 +14,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 +31,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
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[],
) {}
}
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 { AvmExecutionEnvironment } 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: AvmExecutionEnvironment;

/** - */
public readonly calldata: Fr[];
Copy link
Contributor

@fcarreiro fcarreiro Jan 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calldata seems to be both here and in the environment, is that expected/needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In conversations with David I originally deemed that it should be separate from the execution environment, but the more I think about it, it fits the readonly nature of the rest of executionEnvironment, so i will move it in and push

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: AvmExecutionEnvironment) {
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 {
Maddiaa0 marked this conversation as resolved.
Show resolved Hide resolved
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 ?? [],
);
}
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 { 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,8 @@ describe('avm', () => {
const instructions = decodeBytecode(fullBytecode);

// Execute instructions
const context = new AvmMachineState(calldata);
const executionEnvironment = initExecutionEnvironment();
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,27 +1,34 @@
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 executionEnvironment = initExecutionEnvironment();
const context = new AvmMachineState(calldata, executionEnvironment);
const interpreter = new AvmInterpreter(context, stateManager, instructions);
const avmReturnData = interpreter.run();

Expand All @@ -32,13 +39,13 @@ 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 executionEnvironment = initExecutionEnvironment();
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,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 { 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 @@ -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 {
And,
/*Not,*/
Expand All @@ -18,7 +19,7 @@ describe('Bitwise instructions', () => {
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,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 { 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';
import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.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());
expect(machineState.pc).toBe(0);
instruction.execute(innerMachineState, stateManager);
expect(innerMachineState.pc).toBe(1);
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