From db3445e839ab16b52642801f947101548c1ad4ba Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Thu, 25 Jan 2024 00:36:43 +0000 Subject: [PATCH] feat: environment getters --- .../acir-simulator/src/avm/fixtures/index.ts | 22 ++ .../avm/opcodes/environment_getters.test.ts | 111 +++++++ .../src/avm/opcodes/environment_getters.ts | 274 ++++++++++++++++++ 3 files changed, 407 insertions(+) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index e97dc3fa2ce..f30a3b481b5 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -57,3 +57,25 @@ export function initExecutionEnvironment(overrides?: AvmExecutionEnvironmentOver overrides?.calldata ?? [], ); } + +/** + * An interface that allows to override the default values of the GlobalVariables + */ +export interface GlobalVariablesOverrides { + chainId?: Fr; + version?: Fr; + blockNumber?: Fr; + timestamp?: Fr; +} + +/** + * Create an empty instance of the Execution Environment where all values are zero, unless overriden in the overrides object + */ +export function initGlobalVariables(overrides?: GlobalVariablesOverrides): GlobalVariables { + return new GlobalVariables( + overrides?.chainId ?? Fr.zero(), + overrides?.version ?? Fr.zero(), + overrides?.blockNumber ?? Fr.zero(), + overrides?.timestamp ?? Fr.zero(), + ); +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts new file mode 100644 index 00000000000..77cafdae02d --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -0,0 +1,111 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { MockProxy, mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; +import { + Address, + BlockNumber, + ChainId, + FeePerDAGas, + FeePerL1Gas, + FeePerL2Gas, + Origin, + Portal, + Sender, + StorageAddress, + Timestamp, + Version, +} from './environment_getters.js'; + +describe('Environment getters instructions', () => { + let machineState: AvmMachineState; + let journal: MockProxy; + + beforeEach(async () => { + journal = mock(); + }); + + type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; + const envGetterTest = async (key: string, value: Fr, instruction: EnvInstruction) => { + machineState = new AvmMachineState(initExecutionEnvironment({ [key]: value })); + + await instruction.execute(machineState, journal); + const actual = machineState.readMemory(0); + expect(actual).toEqual(value); + }; + + it('Should read address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('address', address, new Address(0)); + }); + + it('Should read storage address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('storageAddress', address, new StorageAddress(0)); + }); + + it('Should read Portal correctly', async () => { + const portal = new Fr(123456n); + await envGetterTest('portal', portal, new Portal(0)); + }); + + it('Should read FeePerL1Gas correctly', async () => { + const feePerL1Gas = new Fr(123456n); + await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(0)); + }); + + it('Should read FeePerL2Gas correctly', async () => { + const feePerL2Gas = new Fr(123456n); + await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(0)); + }); + + it('Should read FeePerDAGas correctly', async () => { + const feePerDaGas = new Fr(123456n); + await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(0)); + }); + + it('Should read Origin correctly', async () => { + const origin = new Fr(123456n); + await envGetterTest('origin', origin, new Origin(0)); + }); + + it('Should read Sender correctly', async () => { + const sender = new Fr(123456n); + await envGetterTest('sender', sender, new Sender(0)); + }); + + describe('Global Variables', () => { + type GlobalsInstruction = ChainId | Version | BlockNumber | Timestamp; + const readGlobalVariableTest = async (key: string, value: Fr, instruction: GlobalsInstruction) => { + const globals = initGlobalVariables({ [key]: value }); + machineState = new AvmMachineState(initExecutionEnvironment({ globals })); + + await instruction.execute(machineState, journal); + const actual = machineState.readMemory(0); + expect(actual).toEqual(value); + }; + + it('Should read chainId', async () => { + const chainId = new Fr(123456n); + await readGlobalVariableTest('chainId', chainId, new ChainId(0)); + }); + + it('Should read version', async () => { + const version = new Fr(123456n); + await readGlobalVariableTest('version', version, new Version(0)); + }); + + it('Should read block number', async () => { + const blockNumber = new Fr(123456n); + await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(0)); + }); + + it('Should read timestamp', async () => { + const timestamp = new Fr(123456n); + await readGlobalVariableTest('timestamp', timestamp, new Timestamp(0)); + }); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts new file mode 100644 index 00000000000..2c8efb97e0a --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -0,0 +1,274 @@ +import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; + +export class Address extends Instruction { + static type: string = 'ADDRESS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { address } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, address); + this.incrementPc(machineState); + } +} + +export class StorageAddress extends Instruction { + static type: string = 'STORAGEADDRESS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { storageAddress } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, storageAddress); + this.incrementPc(machineState); + } +} + +export class Sender extends Instruction { + static type: string = 'SENDER'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { sender } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, sender); + + this.incrementPc(machineState); + } +} + +export class Origin extends Instruction { + static type: string = 'ORIGIN'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { origin } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, origin); + + this.incrementPc(machineState); + } +} + +export class FeePerL1Gas extends Instruction { + static type: string = 'FEEPERL1GAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerL1Gas } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, feePerL1Gas); + + this.incrementPc(machineState); + } +} + +export class FeePerL2Gas extends Instruction { + static type: string = 'FEEPERL2GAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerL2Gas } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, feePerL2Gas); + + this.incrementPc(machineState); + } +} + +export class FeePerDAGas extends Instruction { + static type: string = 'FEEPERDAGAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerDaGas } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, feePerDaGas); + + this.incrementPc(machineState); + } +} + +export class Portal extends Instruction { + static type: string = 'PORTAL'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { portal } = machineState.executionEnvironment; + + machineState.writeMemory(this.destOffset, portal.toField()); + + this.incrementPc(machineState); + } +} + +export class ChainId extends Instruction { + static type: string = 'CHAINID'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { chainId } = machineState.executionEnvironment.globals; + + machineState.writeMemory(this.destOffset, chainId); + + this.incrementPc(machineState); + } +} + +export class Version extends Instruction { + static type: string = 'VERSION'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { version } = machineState.executionEnvironment.globals; + + machineState.writeMemory(this.destOffset, version); + + this.incrementPc(machineState); + } +} + +export class BlockNumber extends Instruction { + static type: string = 'BLOCKNUMBER'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { blockNumber } = machineState.executionEnvironment.globals; + + machineState.writeMemory(this.destOffset, blockNumber); + + this.incrementPc(machineState); + } +} + +export class Timestamp extends Instruction { + static type: string = 'TIMESTAMP'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { timestamp } = machineState.executionEnvironment.globals; + + machineState.writeMemory(this.destOffset, timestamp); + + this.incrementPc(machineState); + } +} + +// export class Coinbase extends Instruction { +// static type: string = 'COINBASE'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {coinbase} = machineState.executionEnvironment.globals; + +// machineState.writeMemory(this.destOffset, coinbase); + +// this.incrementPc(machineState); +// } +// } + +// // TODO: are these even needed within the block? (both block gas limit variables - why does the execution env care?) +// export class BlockL1GasLimit extends Instruction { +// static type: string = 'BLOCKL1GASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockL1GasLimit} = machineState.executionEnvironment.globals; + +// machineState.writeMemory(this.destOffset, blockL1GasLimit); + +// this.incrementPc(machineState); +// } +// } + +// export class BlockL2GasLimit extends Instruction { +// static type: string = 'BLOCKL2GASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockL2GasLimit} = machineState.executionEnvironment.globals; + +// machineState.writeMemory(this.destOffset, blockL2GasLimit); + +// this.incrementPc(machineState); +// } +// } + +// export class BlockDAGasLimit extends Instruction { +// static type: string = 'BLOCKDAGASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockDAGasLimit} = machineState.executionEnvironment.globals; + +// machineState.writeMemory(this.destOffset, blockDAGasLimit); + +// this.incrementPc(machineState); +// } +// }