diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 6967a58fd8f..8963276813b 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -16,6 +16,11 @@ export abstract class MemoryValue { // Use sparingly. public abstract toBigInt(): bigint; + + // To field + public toFr(): Fr { + return new Fr(this.toBigInt()); + } } export abstract class IntegralValue extends MemoryValue { @@ -237,6 +242,11 @@ export class TaggedMemory { return this._mem.slice(offset, offset + size); } + public getSliceAs(offset: number, size: number): T[] { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + return this._mem.slice(offset, offset + size) as T[]; + } + public getSliceTags(offset: number, size: number): TypeTag[] { assert(offset < TaggedMemory.MAX_MEMORY_SIZE); return this._mem.slice(offset, offset + size).map(TaggedMemory.getTag); diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index e97dc3fa2ce..2eb4ac73854 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 Global Variables 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/journal/journal.test.ts b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts index 1d5d0d08671..58b0b3345b9 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.test.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts @@ -86,14 +86,14 @@ describe('journal', () => { describe('UTXOs', () => { it('Should maintain commitments', () => { const utxo = new Fr(1); - journal.writeCommitment(utxo); + journal.writeNoteHash(utxo); const journalUpdates = journal.flush(); - expect(journalUpdates.newCommitments).toEqual([utxo]); + expect(journalUpdates.newNoteHashes).toEqual([utxo]); }); it('Should maintain l1 messages', () => { - const utxo = new Fr(1); + const utxo = [new Fr(1)]; journal.writeL1Message(utxo); const journalUpdates = journal.flush(); @@ -123,16 +123,20 @@ describe('journal', () => { const valueT1 = new Fr(2); const commitment = new Fr(10); const commitmentT1 = new Fr(20); + const logs = [new Fr(1), new Fr(2)]; + const logsT1 = [new Fr(3), new Fr(4)]; journal.writeStorage(contractAddress, key, value); - journal.writeCommitment(commitment); - journal.writeL1Message(commitment); + journal.writeNoteHash(commitment); + journal.writeLog(logs); + journal.writeL1Message(logs); journal.writeNullifier(commitment); const journal1 = new AvmJournal(journal.hostStorage, journal); journal.writeStorage(contractAddress, key, valueT1); - journal.writeCommitment(commitmentT1); - journal.writeL1Message(commitmentT1); + journal.writeNoteHash(commitmentT1); + journal.writeLog(logsT1); + journal.writeL1Message(logsT1); journal.writeNullifier(commitmentT1); journal1.mergeWithParent(); @@ -143,8 +147,9 @@ describe('journal', () => { // Check that the UTXOs are merged const journalUpdates: JournalData = journal.flush(); - expect(journalUpdates.newCommitments).toEqual([commitment, commitmentT1]); - expect(journalUpdates.newL1Messages).toEqual([commitment, commitmentT1]); + expect(journalUpdates.newNoteHashes).toEqual([commitment, commitmentT1]); + expect(journalUpdates.newLogs).toEqual([logs, logsT1]); + expect(journalUpdates.newL1Messages).toEqual([logs, logsT1]); expect(journalUpdates.newNullifiers).toEqual([commitment, commitmentT1]); }); diff --git a/yarn-project/acir-simulator/src/avm/journal/journal.ts b/yarn-project/acir-simulator/src/avm/journal/journal.ts index 4d5b92fec6b..6168ce2cdf2 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.ts @@ -7,11 +7,10 @@ import { HostStorage } from './host_storage.js'; * Data held within the journal */ export type JournalData = { - newCommitments: Fr[]; - - newL1Messages: Fr[]; - + newNoteHashes: Fr[]; newNullifiers: Fr[]; + newL1Messages: Fr[][]; + newLogs: Fr[][]; /** contract address -\> key -\> value */ storageWrites: Map>; }; @@ -34,11 +33,11 @@ export class AvmJournal { private storageReads: Map> = new Map(); // New written state - private newCommitments: Fr[] = []; + private newNoteHashes: Fr[] = []; private newNullifiers: Fr[] = []; - private newL1Message: Fr[] = []; - // New Substrate + // New Substate + private newL1Messages: Fr[][] = []; private newLogs: Fr[][] = []; // contract address -> key -> value @@ -102,27 +101,22 @@ export class AvmJournal { return this.hostStorage.publicStateDb.storageRead(contractAddress, key); } - /** - - * @param commitment - - */ - public writeCommitment(commitment: Fr) { - this.newCommitments.push(commitment); + public writeNoteHash(noteHash: Fr) { + this.newNoteHashes.push(noteHash); } - /** - - * @param message - - */ - public writeL1Message(message: Fr) { - this.newL1Message.push(message); + public writeL1Message(message: Fr[]) { + this.newL1Messages.push(message); } - /** - - * @param nullifier - - */ public writeNullifier(nullifier: Fr) { this.newNullifiers.push(nullifier); } + public writeLog(log: Fr[]) { + this.newLogs.push(log); + } + /** * Merge Journal into parent * - Utxo objects are concatenated @@ -133,26 +127,26 @@ export class AvmJournal { throw new RootJournalCannotBeMerged(); } - const incomingFlush = this.flush(); - // Merge UTXOs - this.parentJournal.newCommitments = this.parentJournal.newCommitments.concat(incomingFlush.newCommitments); - this.parentJournal.newL1Message = this.parentJournal.newL1Message.concat(incomingFlush.newL1Messages); - this.parentJournal.newNullifiers = this.parentJournal.newNullifiers.concat(incomingFlush.newNullifiers); + this.parentJournal.newNoteHashes = this.parentJournal.newNoteHashes.concat(this.newNoteHashes); + this.parentJournal.newL1Messages = this.parentJournal.newL1Messages.concat(this.newL1Messages); + this.parentJournal.newNullifiers = this.parentJournal.newNullifiers.concat(this.newNullifiers); // Merge Public State - mergeContractMaps(this.parentJournal.storageWrites, incomingFlush.storageWrites); + mergeContractMaps(this.parentJournal.storageWrites, this.storageWrites); } - /** Access the current state of the journal + /** + * Access the current state of the journal * - * @returns a JournalData object that can be used to write to the storage + * @returns a JournalData object */ public flush(): JournalData { return { - newCommitments: this.newCommitments, - newL1Messages: this.newL1Message, + newNoteHashes: this.newNoteHashes, newNullifiers: this.newNullifiers, + newL1Messages: this.newL1Messages, + newLogs: this.newLogs, storageWrites: this.storageWrites, }; } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts new file mode 100644 index 00000000000..fd24b23eda4 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -0,0 +1,89 @@ +import { mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { HostStorage } from '../journal/host_storage.js'; +import { AvmJournal } from '../journal/journal.js'; +import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; +import { StaticCallStorageAlterError } from './storage.js'; + +describe('Accrued Substate', () => { + let journal: AvmJournal; + let machineState: AvmMachineState; + + beforeEach(() => { + const hostStorage = mock(); + journal = new AvmJournal(hostStorage); + machineState = new AvmMachineState(initExecutionEnvironment()); + }); + + it('Should append a new note hash correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNoteHash(0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNoteHashes).toEqual(expected); + }); + + it('Should append a new nullifier correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNullifier(0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNullifiers).toEqual(expected); + }); + + it('Should append unencrypted logs correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new EmitUnencryptedLog(startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); + + it('Should append l1 to l2 messages correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new SendL2ToL1Message(startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); + + it('All substate instructions should fail within a static call', async () => { + const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); + machineState = new AvmMachineState(executionEnvironment); + + const instructions = [ + new EmitNoteHash(0), + new EmitNullifier(0), + new EmitUnencryptedLog(0, 1), + new SendL2ToL1Message(0, 1), + ]; + + for (const instruction of instructions) { + const inst = () => instruction.execute(machineState, journal); + await expect(inst()).rejects.toThrowError(StaticCallStorageAlterError); + } + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index e69de29bb2d..de54edaa0c7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -0,0 +1,84 @@ +import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; +import { StaticCallStorageAlterError } from './storage.js'; + +export class EmitNoteHash extends Instruction { + static type: string = 'EMITNOTEHASH'; + static numberOfOperands = 1; + + constructor(private noteHashOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const noteHash = machineState.memory.get(this.noteHashOffset).toFr(); + journal.writeNoteHash(noteHash); + + this.incrementPc(machineState); + } +} + +export class EmitNullifier extends Instruction { + static type: string = 'EMITNULLIFIER'; + static numberOfOperands = 1; + + constructor(private nullifierOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const nullifier = machineState.memory.get(this.nullifierOffset).toFr(); + journal.writeNullifier(nullifier); + + this.incrementPc(machineState); + } +} + +export class EmitUnencryptedLog extends Instruction { + static type: string = 'EMITUNENCRYPTEDLOG'; + static numberOfOperands = 2; + + constructor(private logOffset: number, private logSize: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const log = machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr()); + journal.writeLog(log); + + this.incrementPc(machineState); + } +} + +export class SendL2ToL1Message extends Instruction { + static type: string = 'EMITUNENCRYPTEDLOG'; + static numberOfOperands = 2; + + constructor(private msgOffset: number, private msgSize: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const msg = machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr()); + journal.writeLog(msg); + + this.incrementPc(machineState); + } +} 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..4dd6b0abb85 --- /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.memory.get(0).toFr(); + 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.memory.get(0).toFr(); + 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..18e97575b96 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -0,0 +1,275 @@ +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(this.destOffset, new Field(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.memory.set(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.memory.set(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.memory.set(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.memory.set(this.destOffset, blockDAGasLimit); + +// this.incrementPc(machineState); +// } +// } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index cccef0861fe..434bfc257db 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -20,13 +20,26 @@ export abstract class Instruction { } static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { - for (const off of offsets) { - if (machineState.memory.getTag(off) !== tag) { - const error = `Offset ${off} has tag ${TypeTag[machineState.memory.getTag(off)]}, expected ${TypeTag[tag]}`; - throw new InstructionExecutionError(error); - } + for (const offset of offsets) { + checkTag(machineState, tag, offset); } } + + static checkTagsRange(machineState: AvmMachineState, tag: TypeTag, startOffset: number, size: number) { + for (let offset = startOffset; offset < startOffset + size; offset++) { + checkTag(machineState, tag, offset); + } + } +} + +/** + * Checks that the memory at the given offset has the given tag. + */ +function checkTag(machineState: AvmMachineState, tag: TypeTag, offset: number) { + if (machineState.memory.getTag(offset) !== tag) { + const error = `Offset ${offset} has tag ${TypeTag[machineState.memory.getTag(offset)]}, expected ${TypeTag[tag]}`; + throw new InstructionExecutionError(error); + } } export class InstructionExecutionError extends Error {