diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 5fd240544677..e4e9f49a73cd 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -297,42 +297,6 @@ pub contract AvmTest { } /************************************************************************ -<<<<<<< HEAD -======= - * Hashing functions - ************************************************************************/ - #[public] - fn keccak_hash(data: [u8; 10]) -> [u8; 32] { - std::hash::keccak256(data, data.len() as u32) - } - - #[public] - fn keccak_f1600(data: [u64; 25]) -> [u64; 25] { - std::hash::keccak::keccakf1600(data) - } - - #[public] - fn poseidon2_hash(data: [Field; 10]) -> Field { - std::hash::poseidon2::Poseidon2::hash(data, data.len()) - } - - #[public] - fn sha256_hash(data: [u8; 10]) -> [u8; 32] { - sha256::digest(data) - } - - #[public] - fn pedersen_hash(data: [Field; 10]) -> Field { - std::hash::pedersen_hash(data) - } - - #[public] - fn pedersen_hash_with_index(data: [Field; 10]) -> Field { - std::hash::pedersen_hash_with_separator(data, /*index=*/ 20) - } - - /************************************************************************ ->>>>>>> master * Contract instance ************************************************************************/ #[public] diff --git a/yarn-project/simulator/src/avm/avm_machine_state.ts b/yarn-project/simulator/src/avm/avm_machine_state.ts index 1ba4fa601b9a..01afec201e25 100644 --- a/yarn-project/simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/simulator/src/avm/avm_machine_state.ts @@ -1,6 +1,6 @@ import { type Fr } from '@aztec/circuits.js'; -import { GAS_DIMENSIONS, type Gas } from './avm_gas.js'; +import { type Gas } from './avm_gas.js'; import { TaggedMemory } from './avm_memory_types.js'; import { type AvmRevertReason, OutOfGasError } from './errors.js'; @@ -92,26 +92,29 @@ export class AvmMachineState { */ public consumeGas(gasCost: Partial) { // Assert there is enough gas on every dimension. - const outOfGasDimensions = GAS_DIMENSIONS.filter( - dimension => this[`${dimension}Left`] - (gasCost[dimension] ?? 0) < 0, - ); + const outOfL2Gas = this.l2GasLeft - (gasCost.l2Gas ?? 0) < 0; + const outOfDaGas = this.daGasLeft - (gasCost.daGas ?? 0) < 0; // If not, trigger an exceptional halt. - // See https://yp-aztec.netlify.app/docs/public-vm/execution#gas-checks-and-tracking - if (outOfGasDimensions.length > 0) { + if (outOfL2Gas || outOfDaGas) { this.exceptionalHalt(); - throw new OutOfGasError(outOfGasDimensions); + const dimensions = []; + if (outOfL2Gas) { + dimensions.push('l2Gas'); + } + if (outOfDaGas) { + dimensions.push('daGas'); + } + throw new OutOfGasError(dimensions); } // Otherwise, charge the corresponding gas - for (const dimension of GAS_DIMENSIONS) { - this[`${dimension}Left`] -= gasCost[dimension] ?? 0; - } + this.l2GasLeft -= gasCost.l2Gas ?? 0; + this.daGasLeft -= gasCost.daGas ?? 0; } /** Increases the gas left by the amounts specified. */ public refundGas(gasRefund: Partial) { - for (const dimension of GAS_DIMENSIONS) { - this[`${dimension}Left`] += gasRefund[dimension] ?? 0; - } + this.l2GasLeft += gasRefund.l2Gas ?? 0; + this.daGasLeft += gasRefund.daGas ?? 0; } /** @@ -151,7 +154,8 @@ export class AvmMachineState { * Flag an exceptional halt. Clears gas left and sets the reverted flag. No output data. */ private exceptionalHalt() { - GAS_DIMENSIONS.forEach(dimension => (this[`${dimension}Left`] = 0)); + this.l2GasLeft = 0; + this.daGasLeft = 0; this.reverted = true; this.halted = true; } diff --git a/yarn-project/simulator/src/avm/avm_memory_types.test.ts b/yarn-project/simulator/src/avm/avm_memory_types.test.ts index 8d1146d1021d..91490ff4c9c0 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.test.ts @@ -1,16 +1,6 @@ import { AssertionError } from 'assert'; -import { - Field, - MeteredTaggedMemory, - TaggedMemory, - Uint1, - Uint8, - Uint16, - Uint32, - Uint64, - Uint128, -} from './avm_memory_types.js'; +import { Field, TaggedMemory, Uint1, Uint8, Uint16, Uint32, Uint64, Uint128 } from './avm_memory_types.js'; describe('TaggedMemory', () => { it('Elements should be Field(0) after construction', () => { @@ -67,58 +57,6 @@ describe('TaggedMemory', () => { }); }); -describe('MeteredTaggedMemory', () => { - let mem: MeteredTaggedMemory; - - beforeEach(() => { - mem = new MeteredTaggedMemory(new TaggedMemory()); - }); - - it(`Counts reads`, () => { - mem.get(10); - mem.getAs(20); - expect(mem.reset()).toEqual({ reads: 2, writes: 0 }); - }); - - it(`Counts reading slices`, () => { - const val = [new Field(5), new Field(6), new Field(7)]; - mem.setSlice(10, val); - mem.reset(); - - mem.getSlice(10, 3); - mem.getSliceAs(11, 2); - expect(mem.reset()).toEqual({ reads: 5, writes: 0 }); - }); - - it(`Counts writes`, () => { - mem.set(10, new Uint8(5)); - expect(mem.reset()).toEqual({ reads: 0, writes: 1 }); - }); - - it(`Counts writing slices`, () => { - mem.setSlice(10, [new Field(5), new Field(6)]); - expect(mem.reset()).toEqual({ reads: 0, writes: 2 }); - }); - - it(`Clears stats`, () => { - mem.get(10); - mem.set(20, new Uint8(5)); - expect(mem.reset()).toEqual({ reads: 1, writes: 1 }); - expect(mem.reset()).toEqual({ reads: 0, writes: 0 }); - }); - - it(`Asserts stats`, () => { - mem.get(10); - mem.set(20, new Uint8(5)); - expect(() => mem.assert({ reads: 1, writes: 1 })).not.toThrow(); - }); - - it(`Throws on failed stat assertion`, () => { - mem.get(10); - expect(() => mem.assert({ reads: 1, writes: 1 })).toThrow(); - }); -}); - type IntegralClass = typeof Uint1 | typeof Uint8 | typeof Uint16 | typeof Uint32 | typeof Uint64 | typeof Uint128; describe.each([Uint1])('Integral Types (U1 only)', (clsValue: IntegralClass) => { diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index 9b3dd8d64eb2..40e91bbcd053 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -15,13 +15,7 @@ import { type FunctionsOf } from '@aztec/foundation/types'; import { strict as assert } from 'assert'; -import { - InstructionExecutionError, - InvalidTagValueError, - MemorySliceOutOfRangeError, - TagCheckError, -} from './errors.js'; -import { Addressing, AddressingMode } from './opcodes/addressing_mode.js'; +import { InvalidTagValueError, MemorySliceOutOfRangeError, TagCheckError } from './errors.js'; /** MemoryValue gathers the common operations for all memory types. */ export abstract class MemoryValue { @@ -251,22 +245,14 @@ export class TaggedMemory implements TaggedMemoryInterface { return TaggedMemory.MAX_MEMORY_SIZE; } - /** Returns a MeteredTaggedMemory instance to track the number of reads and writes if TRACK_MEMORY_ACCESSES is set. */ - public track(type: string = 'instruction'): TaggedMemoryInterface { - return TaggedMemory.TRACK_MEMORY_ACCESSES ? new MeteredTaggedMemory(this, type) : this; - } - public get(offset: number): MemoryValue { - assert(offset < TaggedMemory.MAX_MEMORY_SIZE); - const value = this.getAs(offset); - return value; + return this.getAs(offset); } public getAs(offset: number): T { - assert(Number.isInteger(offset)); - assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + assert(Number.isInteger(offset) && offset < TaggedMemory.MAX_MEMORY_SIZE); const word = this._mem.get(offset); - TaggedMemory.log.trace(`get(${offset}) = ${word}`); + //TaggedMemory.log.trace(`get(${offset}) = ${word}`); if (word === undefined) { TaggedMemory.log.debug(`WARNING: Memory at offset ${offset} is undefined!`); return new Field(0) as T; @@ -300,10 +286,9 @@ export class TaggedMemory implements TaggedMemoryInterface { } public set(offset: number, v: MemoryValue) { - assert(Number.isInteger(offset)); - assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + assert(Number.isInteger(offset) && offset < TaggedMemory.MAX_MEMORY_SIZE); this._mem.set(offset, v); - TaggedMemory.log.trace(`set(${offset}, ${v})`); + //TaggedMemory.log.trace(`set(${offset}, ${v})`); } public setSlice(offset: number, slice: MemoryValue[]) { @@ -320,8 +305,7 @@ export class TaggedMemory implements TaggedMemoryInterface { } public getTag(offset: number): TypeTag { - assert(Number.isInteger(offset)); - assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + assert(Number.isInteger(offset) && offset < TaggedMemory.MAX_MEMORY_SIZE); return TaggedMemory.getTag(this._mem.get(offset)); } @@ -340,25 +324,13 @@ export class TaggedMemory implements TaggedMemoryInterface { } public static checkIsIntegralTag(tag: TypeTag) { - if ( - ![TypeTag.UINT1, TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128].includes(tag) - ) { + if (!INTEGRAL_TAGS.has(tag)) { throw TagCheckError.forTag(TypeTag[tag], 'integral'); } } public static checkIsValidTag(tagNumber: number) { - if ( - ![ - TypeTag.FIELD, - TypeTag.UINT1, - TypeTag.UINT8, - TypeTag.UINT16, - TypeTag.UINT32, - TypeTag.UINT64, - TypeTag.UINT128, - ].includes(tagNumber) - ) { + if (!VALID_TAGS.has(tagNumber)) { throw new InvalidTagValueError(tagNumber); } } @@ -375,11 +347,9 @@ export class TaggedMemory implements TaggedMemoryInterface { /** * Check that all tags at the given offsets are the same. */ - public checkTagsAreSame(...offsets: number[]) { - const tag = this.getTag(offsets[0]); - for (let i = 1; i < offsets.length; i++) { - this.checkTag(tag, offsets[i]); - } + public checkTagsAreSame(offset0: number, offset1: number) { + const tag0 = this.getTag(offset0); + this.checkTag(tag0, offset1); } /** @@ -391,32 +361,12 @@ export class TaggedMemory implements TaggedMemoryInterface { } } - // TODO: this might be slow, but I don't want to have the types know of their tags. - // It might be possible to have a map. public static getTag(v: MemoryValue | undefined): TypeTag { - let tag = TypeTag.INVALID; - - // Not sure why, but using instanceof here doesn't work and leads odd behavior, - // but using constructor.name does the job... if (v === undefined) { - tag = TypeTag.FIELD; // uninitialized memory is Field(0) - } else if (v.constructor.name == 'Field') { - tag = TypeTag.FIELD; - } else if (v.constructor.name == 'Uint1') { - tag = TypeTag.UINT1; - } else if (v.constructor.name == 'Uint8') { - tag = TypeTag.UINT8; - } else if (v.constructor.name == 'Uint16') { - tag = TypeTag.UINT16; - } else if (v.constructor.name == 'Uint32') { - tag = TypeTag.UINT32; - } else if (v.constructor.name == 'Uint64') { - tag = TypeTag.UINT64; - } else if (v.constructor.name == 'Uint128') { - tag = TypeTag.UINT128; + return TypeTag.FIELD; // uninitialized memory is Field(0) + } else { + return TAG_FOR_MEM_VAL.get(v.constructor.name) ?? TypeTag.INVALID; } - - return tag; } // Truncates the value to fit the type. @@ -441,123 +391,33 @@ export class TaggedMemory implements TaggedMemoryInterface { throw new InvalidTagValueError(tag); } } - - /** No-op. Implemented here for compatibility with the MeteredTaggedMemory. */ - public assert(_operations: Partial) {} -} - -/** Tagged memory wrapper with metering for each memory read and write operation. */ -export class MeteredTaggedMemory implements TaggedMemoryInterface { - private reads: number = 0; - private writes: number = 0; - - constructor(private wrapped: TaggedMemory, private type: string = 'instruction') {} - - /** Returns the number of reads and writes tracked so far and resets them to zero. */ - public reset(): MemoryOperations { - const stats = { reads: this.reads, writes: this.writes }; - this.reads = 0; - this.writes = 0; - return stats; - } - - /** - * Asserts that the exact number of memory operations have been performed. - * Indirect represents the flags for indirect accesses: each bit set to one counts as an extra read. - */ - public assert(operations: Partial) { - const { - reads: expectedReads, - writes: expectedWrites, - addressing, - } = { reads: 0, writes: 0, addressing: new Addressing([]), ...operations }; - - const totalExpectedReads = - expectedReads + addressing.count(AddressingMode.INDIRECT) + addressing.count(AddressingMode.RELATIVE); - const { reads: actualReads, writes: actualWrites } = this.reset(); - if (actualReads !== totalExpectedReads) { - throw new InstructionExecutionError( - `Incorrect number of memory reads for ${this.type}: expected ${totalExpectedReads} but executed ${actualReads}`, - ); - } - if (actualWrites !== expectedWrites) { - throw new InstructionExecutionError( - `Incorrect number of memory writes for ${this.type}: expected ${expectedWrites} but executed ${actualWrites}`, - ); - } - } - - public getMaxMemorySize(): number { - return this.wrapped.getMaxMemorySize(); - } - - public track(type: string = 'instruction'): MeteredTaggedMemory { - return new MeteredTaggedMemory(this.wrapped, type); - } - - public get(offset: number): MemoryValue { - this.reads++; - return this.wrapped.get(offset); - } - - public getSliceAs(offset: number, size: number): T[] { - this.reads += size; - return this.wrapped.getSliceAs(offset, size); - } - - public getAs(offset: number): T { - this.reads++; - return this.wrapped.getAs(offset); - } - - public getSlice(offset: number, size: number): MemoryValue[] { - this.reads += size; - return this.wrapped.getSlice(offset, size); - } - - public set(offset: number, v: MemoryValue): void { - this.writes++; - this.wrapped.set(offset, v); - } - - public setSlice(offset: number, vs: MemoryValue[]): void { - this.writes += vs.length; - this.wrapped.setSlice(offset, vs); - } - - public getSliceTags(offset: number, size: number): TypeTag[] { - return this.wrapped.getSliceTags(offset, size); - } - - public getTag(offset: number): TypeTag { - return this.wrapped.getTag(offset); - } - - public checkTag(tag: TypeTag, offset: number): void { - this.wrapped.checkTag(tag, offset); - } - - public checkIsValidMemoryOffsetTag(offset: number): void { - this.wrapped.checkIsValidMemoryOffsetTag(offset); - } - - public checkTags(tag: TypeTag, ...offsets: number[]): void { - this.wrapped.checkTags(tag, ...offsets); - } - - public checkTagsAreSame(...offsets: number[]): void { - this.wrapped.checkTagsAreSame(...offsets); - } - - public checkTagsRange(tag: TypeTag, startOffset: number, size: number): void { - this.wrapped.checkTagsRange(tag, startOffset, size); - } } -/** Tracks number of memory reads and writes. */ -export type MemoryOperations = { - /** How many total reads are performed. Slice reads are count as one per element. */ - reads: number; - /** How many total writes are performed. Slice writes are count as one per element. */ - writes: number; -}; +const TAG_FOR_MEM_VAL = new Map([ + ['Field', TypeTag.FIELD], + ['Uint1', TypeTag.UINT1], + ['Uint8', TypeTag.UINT8], + ['Uint16', TypeTag.UINT16], + ['Uint32', TypeTag.UINT32], + ['Uint64', TypeTag.UINT64], + ['Uint128', TypeTag.UINT128], +]); + +const VALID_TAGS = new Set([ + TypeTag.FIELD, + TypeTag.UINT1, + TypeTag.UINT8, + TypeTag.UINT16, + TypeTag.UINT32, + TypeTag.UINT64, + TypeTag.UINT128, +]); + +const INTEGRAL_TAGS = new Set([ + TypeTag.UINT1, + TypeTag.UINT8, + TypeTag.UINT16, + TypeTag.UINT32, + TypeTag.UINT64, + TypeTag.UINT128, +]); diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index 48bc92beb152..f29e62e14f47 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -18,6 +18,7 @@ import { revertReasonFromExplicitRevert, } from './errors.js'; import { type AvmPersistableStateManager } from './journal/journal.js'; +import { type Instruction } from './opcodes/instruction.js'; import { INSTRUCTION_SET, type InstructionSet, @@ -33,20 +34,26 @@ export class AvmSimulator { private log: Logger; private bytecode: Buffer | undefined; private opcodeTallies: Map = new Map(); + // maps pc to [instr, bytesRead] + private deserializedInstructionsCache: Map = new Map(); private tallyPrintFunction = () => {}; private tallyInstructionFunction = (_b: string, _c: Gas) => {}; // Test Purposes only: Logger will not have the proper function name. Use this constructor for testing purposes // only. Otherwise, use build() below. - constructor(private context: AvmContext, private instructionSet: InstructionSet = INSTRUCTION_SET()) { + constructor( + private context: AvmContext, + private instructionSet: InstructionSet = INSTRUCTION_SET(), + enableTallying = false, + ) { assert( context.machineState.gasLeft.l2Gas <= MAX_L2_GAS_PER_TX_PUBLIC_PORTION, `Cannot allocate more than ${MAX_L2_GAS_PER_TX_PUBLIC_PORTION} to the AVM for execution.`, ); this.log = createLogger(`simulator:avm(calldata[0]: ${context.environment.calldata[0]})`); - // TODO(palla/log): Should tallies be printed on debug, or only on trace? - if (this.log.isLevelEnabled('debug')) { + // Turn on tallying if explicitly enabled or if trace logging + if (enableTallying || this.log.isLevelEnabled('trace')) { this.tallyPrintFunction = this.printOpcodeTallies; this.tallyInstructionFunction = this.tallyInstruction; } @@ -125,6 +132,7 @@ export class AvmSimulator { * This method is useful for testing and debugging. */ public async executeBytecode(bytecode: Buffer): Promise { + const startTotalTime = performance.now(); assert(isAvmBytecode(bytecode), "AVM simulator can't execute non-AVM bytecode"); assert(bytecode.length > 0, "AVM simulator can't execute empty bytecode"); @@ -137,19 +145,32 @@ export class AvmSimulator { // continuing until the machine state signifies a halt let instrCounter = 0; while (!machineState.getHalted()) { - const [instruction, bytesRead] = decodeInstructionFromBytecode(bytecode, machineState.pc, this.instructionSet); + // Get the instruction from cache, or deserialize for the first time + let cachedInstruction = this.deserializedInstructionsCache.get(machineState.pc); + + if (cachedInstruction === undefined) { + cachedInstruction = decodeInstructionFromBytecode(bytecode, machineState.pc, this.instructionSet); + this.deserializedInstructionsCache.set(machineState.pc, cachedInstruction); + } + const [instruction, bytesRead] = cachedInstruction; + const instrStartGas = machineState.gasLeft; // Save gas before executing instruction (for profiling) - this.log.trace( - `[PC:${machineState.pc}] [IC:${instrCounter++}] ${instruction.toString()} (gasLeft l2=${ - machineState.l2GasLeft - } da=${machineState.daGasLeft})`, - ); + if (this.log.isLevelEnabled('trace')) { + // Skip this entirely to avoid toStringing etc if trace is not enabled + this.log.trace( + `[PC:${machineState.pc}] [IC:${instrCounter}] ${instruction.toString()} (gasLeft l2=${ + machineState.l2GasLeft + } da=${machineState.daGasLeft})`, + ); + } + instrCounter++; + + machineState.nextPc = machineState.pc + bytesRead; + // Execute the instruction. // Normal returns and reverts will return normally here. // "Exceptional halts" will throw. - machineState.nextPc = machineState.pc + bytesRead; - await instruction.execute(this.context); if (!instruction.handlesPC()) { // Increment PC if the instruction doesn't handle it itself @@ -181,6 +202,11 @@ export class AvmSimulator { this.log.debug(`Executed ${instrCounter} instructions and consumed ${totalGasUsed.l2Gas} L2 Gas`); this.tallyPrintFunction(); + + const endTotalTime = performance.now(); + const totalTime = endTotalTime - startTotalTime; + this.log.debug(`Total execution time: ${totalTime}ms`); + // Return results for processing by calling context return results; } catch (err: any) { diff --git a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts index 4c0a0c88173a..60aa40ede1dd 100644 --- a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts @@ -28,7 +28,7 @@ export class NoteHashExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.noteHashOffset, this.leafIndexOffset, this.existsOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); @@ -41,8 +41,6 @@ export class NoteHashExists extends Instruction { const exists = await context.persistableState.checkNoteHashExists(context.environment.address, noteHash, leafIndex); memory.set(existsOffset, exists ? new Uint1(1) : new Uint1(0)); - - memory.assert({ reads: 2, writes: 1, addressing }); } } @@ -57,7 +55,7 @@ export class EmitNoteHash extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.noteHashOffset]; @@ -71,8 +69,6 @@ export class EmitNoteHash extends Instruction { const noteHash = memory.get(noteHashOffset).toFr(); await context.persistableState.writeNoteHash(context.environment.address, noteHash); - - memory.assert({ reads: 1, addressing }); } } @@ -98,7 +94,7 @@ export class NullifierExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.nullifierOffset, this.addressOffset, this.existsOffset]; @@ -111,8 +107,6 @@ export class NullifierExists extends Instruction { const exists = await context.persistableState.checkNullifierExists(address, nullifier); memory.set(existsOffset, exists ? new Uint1(1) : new Uint1(0)); - - memory.assert({ reads: 2, writes: 1, addressing }); } } @@ -131,7 +125,7 @@ export class EmitNullifier extends Instruction { throw new StaticCallAlterationError(); } - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.nullifierOffset]; @@ -152,8 +146,6 @@ export class EmitNullifier extends Instruction { throw e; } } - - memory.assert({ reads: 1, addressing }); } } @@ -179,7 +171,7 @@ export class L1ToL2MessageExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.msgHashOffset, this.msgLeafIndexOffset, this.existsOffset]; @@ -195,8 +187,6 @@ export class L1ToL2MessageExists extends Instruction { msgLeafIndex, ); memory.set(existsOffset, exists ? new Uint1(1) : new Uint1(0)); - - memory.assert({ reads: 2, writes: 1, addressing }); } } @@ -216,7 +206,7 @@ export class EmitUnencryptedLog extends Instruction { throw new StaticCallAlterationError(); } - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.logOffset, this.logSizeOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); @@ -230,8 +220,6 @@ export class EmitUnencryptedLog extends Instruction { context.machineState.consumeGas(this.gasCost(logSize)); const log = memory.getSlice(logOffset, logSize).map(f => f.toFr()); context.persistableState.writePublicLog(contractAddress, log); - - memory.assert({ reads: 1 + logSize, addressing }); } } @@ -250,7 +238,7 @@ export class SendL2ToL1Message extends Instruction { throw new StaticCallAlterationError(); } - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.recipientOffset, this.contentOffset]; @@ -261,7 +249,5 @@ export class SendL2ToL1Message extends Instruction { const recipient = memory.get(recipientOffset).toFr(); const content = memory.get(contentOffset).toFr(); context.persistableState.writeL2ToL1Message(context.environment.address, recipient, content); - - memory.assert({ reads: 2, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts index c7473bcaf8d6..529fec777cf2 100644 --- a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts +++ b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts @@ -59,12 +59,19 @@ export class Addressing { public resolve(offsets: number[], mem: TaggedMemoryInterface): number[] { assert(offsets.length <= this.modePerOperand.length); const resolved = new Array(offsets.length); + + let didRelativeOnce = false; + let baseAddr = 0; + for (const [i, offset] of offsets.entries()) { const mode = this.modePerOperand[i]; resolved[i] = offset; if (mode & AddressingMode.RELATIVE) { - mem.checkIsValidMemoryOffsetTag(0); - const baseAddr = Number(mem.get(0).toBigInt()); + if (!didRelativeOnce) { + mem.checkIsValidMemoryOffsetTag(0); + baseAddr = Number(mem.get(0).toBigInt()); + didRelativeOnce = true; + } resolved[i] += baseAddr; if (resolved[i] >= TaggedMemory.MAX_MEMORY_SIZE) { throw new RelativeAddressOutOfRangeError(baseAddr, offset); diff --git a/yarn-project/simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/simulator/src/avm/opcodes/arithmetic.ts index 0b5c88a92cb3..d5a76f91896b 100644 --- a/yarn-project/simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/simulator/src/avm/opcodes/arithmetic.ts @@ -13,7 +13,7 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.aOffset, this.bOffset, this.dstOffset]; @@ -26,8 +26,6 @@ export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInst const dest = this.compute(a, b); memory.set(dstOffset, dest); - - memory.assert({ reads: 2, writes: 1, addressing }); } protected abstract compute(a: MemoryValue, b: MemoryValue): MemoryValue; diff --git a/yarn-project/simulator/src/avm/opcodes/bitwise.ts b/yarn-project/simulator/src/avm/opcodes/bitwise.ts index 5e36c158212c..793c1f047e97 100644 --- a/yarn-project/simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/simulator/src/avm/opcodes/bitwise.ts @@ -7,7 +7,7 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.aOffset, this.bOffset, this.dstOffset]; @@ -20,8 +20,6 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction { const res = this.compute(a, b); memory.set(dstOffset, res); - - memory.assert({ reads: 2, writes: 1, addressing }); } protected abstract compute(a: IntegralValue, b: IntegralValue): IntegralValue; @@ -96,7 +94,7 @@ export class Not extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.srcOffset, this.dstOffset]; @@ -107,7 +105,5 @@ export class Not extends Instruction { const res = value.not(); memory.set(dstOffset, res); - - memory.assert({ reads: 1, writes: 1, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.ts b/yarn-project/simulator/src/avm/opcodes/comparators.ts index 7dd54f9b663a..2a683b5a0bba 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.ts @@ -6,7 +6,7 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; abstract class ComparatorInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.aOffset, this.bOffset, this.dstOffset]; @@ -19,8 +19,6 @@ abstract class ComparatorInstruction extends ThreeOperandInstruction { const dest = new Uint1(this.compare(a, b) ? 1 : 0); memory.set(dstOffset, dest); - - memory.assert({ reads: 2, writes: 1, addressing }); } protected abstract compare(a: MemoryValue, b: MemoryValue): boolean; diff --git a/yarn-project/simulator/src/avm/opcodes/contract.ts b/yarn-project/simulator/src/avm/opcodes/contract.ts index f136b1e0bb3a..6874ddeedd3f 100644 --- a/yarn-project/simulator/src/avm/opcodes/contract.ts +++ b/yarn-project/simulator/src/avm/opcodes/contract.ts @@ -35,7 +35,7 @@ export class GetContractInstance extends Instruction { } async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); if (!(this.memberEnum in ContractInstanceMember)) { @@ -68,7 +68,5 @@ export class GetContractInstance extends Instruction { memory.set(existsOffset, new Uint1(exists ? 1 : 0)); memory.set(dstOffset, memberValue); - - memory.assert({ reads: 1, writes: 2, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/control_flow.ts b/yarn-project/simulator/src/avm/opcodes/control_flow.ts index 2bb17c1e8baa..f9097b6119bf 100644 --- a/yarn-project/simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/simulator/src/avm/opcodes/control_flow.ts @@ -19,8 +19,6 @@ export class Jump extends Instruction { context.machineState.consumeGas(this.gasCost()); context.machineState.pc = this.jumpOffset; - - context.machineState.memory.assert({}); } public override handlesPC(): boolean { @@ -45,7 +43,7 @@ export class JumpI extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.condOffset]; @@ -58,8 +56,6 @@ export class JumpI extends Instruction { } else { context.machineState.pc = this.loc; } - - memory.assert({ reads: 1, addressing }); } public override handlesPC(): boolean { @@ -85,8 +81,6 @@ export class InternalCall extends Instruction { returnPc: context.machineState.nextPc, }); context.machineState.pc = this.loc; - - context.machineState.memory.assert({}); } public override handlesPC(): boolean { @@ -112,8 +106,6 @@ export class InternalReturn extends Instruction { throw new InstructionExecutionError('Internal call stack empty!'); } context.machineState.pc = stackEntry.returnPc; - - context.machineState.memory.assert({}); } public override handlesPC(): boolean { diff --git a/yarn-project/simulator/src/avm/opcodes/conversion.ts b/yarn-project/simulator/src/avm/opcodes/conversion.ts index 60de7a8db080..3d32d643d4dd 100644 --- a/yarn-project/simulator/src/avm/opcodes/conversion.ts +++ b/yarn-project/simulator/src/avm/opcodes/conversion.ts @@ -32,7 +32,7 @@ export class ToRadixBE extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.srcOffset, this.radixOffset, this.numLimbsOffset, this.outputBitsOffset, this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [srcOffset, radixOffset, numLimbsOffset, outputBitsOffset, dstOffset] = addressing.resolve(operands, memory); @@ -76,7 +76,5 @@ export class ToRadixBE extends Instruction { const outputType = outputBits != 0 ? Uint1 : Uint8; const res = limbArray.map(byte => new outputType(byte)); memory.setSlice(dstOffset, res); - - memory.assert({ reads: 4, writes: numLimbs, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/ec_add.ts b/yarn-project/simulator/src/avm/opcodes/ec_add.ts index e358342c0639..0e261febca38 100644 --- a/yarn-project/simulator/src/avm/opcodes/ec_add.ts +++ b/yarn-project/simulator/src/avm/opcodes/ec_add.ts @@ -38,7 +38,7 @@ export class EcAdd extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [ @@ -89,7 +89,5 @@ export class EcAdd extends Instruction { memory.setSlice(dstOffset, [new Field(dest.x), new Field(dest.y)]); // Check representation of infinity for grumpkin memory.setSlice(dstOffset + 2, [new Uint1(dest.equals(Point.ZERO) ? 1 : 0)]); - - memory.assert({ reads: 6, writes: 3, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts index 97c78488d1bd..b552f25fe0a3 100644 --- a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts @@ -66,7 +66,7 @@ export class GetEnvVar extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); if (!(this.varEnum in EnvironmentVariable)) { @@ -78,7 +78,5 @@ export class GetEnvVar extends Instruction { const [dstOffset] = addressing.resolve(operands, memory); memory.set(dstOffset, getValue(this.varEnum as EnvironmentVariable, context)); - - memory.assert({ writes: 1, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.ts index 22cd3c12385d..d8884a274c2b 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.ts @@ -30,7 +30,7 @@ abstract class ExternalCall extends Instruction { } public async execute(context: AvmContext) { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.gasOffset, this.addrOffset, this.argsOffset, this.argsSizeOffset, this.successOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [gasOffset, addrOffset, argsOffset, argsSizeOffset, successOffset] = addressing.resolve(operands, memory); @@ -94,7 +94,6 @@ abstract class ExternalCall extends Instruction { } else { context.persistableState.reject(nestedContext.persistableState); } - memory.assert({ reads: calldataSize + 4, writes: 1, addressing }); } public abstract override get type(): 'CALL' | 'STATICCALL'; @@ -134,7 +133,7 @@ export class Return extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.returnOffset, this.returnSizeOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); @@ -147,7 +146,6 @@ export class Return extends Instruction { const output = memory.getSlice(returnOffset, returnSize).map(word => word.toFr()); context.machineState.return(output); - memory.assert({ reads: returnSize + 1, addressing }); } public override handlesPC(): boolean { @@ -177,7 +175,7 @@ export class Revert extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.returnOffset, this.retSizeOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); @@ -189,7 +187,6 @@ export class Revert extends Instruction { const output = memory.getSlice(returnOffset, retSize).map(word => word.toFr()); context.machineState.revert(output); - memory.assert({ reads: retSize + 1, addressing }); } // We don't want to increase the PC after reverting because it breaks messages. diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index c2d32a376e3d..f91876240019 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -24,7 +24,7 @@ export class Poseidon2 extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.inputStateOffset, this.outputStateOffset]; @@ -39,8 +39,6 @@ export class Poseidon2 extends Instruction { outputOffset, outputState.map(word => new Field(word)), ); - - memory.assert({ reads: Poseidon2.stateSize, writes: Poseidon2.stateSize, addressing }); } } @@ -63,7 +61,7 @@ export class KeccakF1600 extends Instruction { // pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] public async execute(context: AvmContext): Promise { const inputSize = 25; - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.dstOffset, this.inputOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [dstOffset, inputOffset] = addressing.resolve(operands, memory); @@ -76,8 +74,6 @@ export class KeccakF1600 extends Instruction { const res = updatedState.map(word => new Uint64(word)); memory.setSlice(dstOffset, res); - - memory.assert({ reads: inputSize, writes: inputSize, addressing }); } } @@ -107,7 +103,7 @@ export class Sha256Compression extends Instruction { const STATE_SIZE = 8; const INPUTS_SIZE = 16; - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.outputOffset, this.stateOffset, this.inputsOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [outputOffset, stateOffset, inputsOffset] = addressing.resolve(operands, memory); @@ -125,7 +121,5 @@ export class Sha256Compression extends Instruction { // Conversion required from Uint32Array to Uint32[] (can't map directly, need `...`) const res = [...output].map(word => new Uint32(word)); memory.setSlice(outputOffset, res); - - memory.assert({ reads: STATE_SIZE + INPUTS_SIZE, writes: STATE_SIZE, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/memory.ts b/yarn-project/simulator/src/avm/opcodes/memory.ts index e1e1cf9b2feb..9420c03b4884 100644 --- a/yarn-project/simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/simulator/src/avm/opcodes/memory.ts @@ -66,15 +66,13 @@ export class Set extends Instruction { // Constructor ensured that this.inTag is a valid tag const res = TaggedMemory.buildFromTagTruncating(this.value, this.inTag); - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [dstOffset] = addressing.resolve(operands, memory); memory.set(dstOffset, res); - - memory.assert({ writes: 1, addressing }); } } @@ -103,7 +101,7 @@ export class Cast extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.srcOffset, this.dstOffset]; @@ -115,8 +113,6 @@ export class Cast extends Instruction { const casted = TaggedMemory.buildFromTagTruncating(a.toBigInt(), this.dstTag); memory.set(dstOffset, casted); - - memory.assert({ reads: 1, writes: 1, addressing }); } } @@ -143,18 +139,14 @@ export class Mov extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.srcOffset, this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [srcOffset, dstOffset] = addressing.resolve(operands, memory); - const a = memory.get(srcOffset); - memory.set(dstOffset, a); - - memory.assert({ reads: 1, writes: 1, addressing }); } } @@ -180,7 +172,7 @@ export class CalldataCopy extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.cdStartOffset, this.copySizeOffset, this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [cdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory); @@ -196,8 +188,6 @@ export class CalldataCopy extends Instruction { const transformedData = [...slice, ...Array(copySize - slice.length).fill(new Field(0))]; memory.setSlice(dstOffset, transformedData); - - memory.assert({ reads: 2, writes: copySize, addressing }); } } @@ -212,15 +202,13 @@ export class ReturndataSize extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [dstOffset] = addressing.resolve(operands, memory); context.machineState.consumeGas(this.gasCost()); memory.set(dstOffset, new Uint32(context.machineState.nestedReturndata.length)); - - memory.assert({ writes: 1, addressing }); } } @@ -246,7 +234,7 @@ export class ReturndataCopy extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.rdStartOffset, this.copySizeOffset, this.dstOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [rdStartOffset, copySizeOffset, dstOffset] = addressing.resolve(operands, memory); @@ -262,7 +250,5 @@ export class ReturndataCopy extends Instruction { const transformedData = [...slice, ...Array(copySize - slice.length).fill(new Field(0))]; memory.setSlice(dstOffset, transformedData); - - memory.assert({ reads: 2, writes: copySize, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/misc.ts b/yarn-project/simulator/src/avm/opcodes/misc.ts index 09d095a05ee8..f1d9f3743643 100644 --- a/yarn-project/simulator/src/avm/opcodes/misc.ts +++ b/yarn-project/simulator/src/avm/opcodes/misc.ts @@ -32,7 +32,7 @@ export class DebugLog extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; const operands = [this.messageOffset, this.fieldsOffset, this.fieldsSizeOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); const [messageOffset, fieldsOffset, fieldsSizeOffset] = addressing.resolve(operands, memory); @@ -56,7 +56,5 @@ export class DebugLog extends Instruction { ); DebugLog.logger.verbose(formattedStr); - - memory.assert({ reads: 1 + fieldsSize + this.messageSize, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts b/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts index 4b73beba502e..7535fde76e05 100644 --- a/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts +++ b/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts @@ -33,7 +33,7 @@ export class MultiScalarMul extends Instruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; // Resolve indirects const operands = [this.pointsOffset, this.scalarsOffset, this.outputOffset, this.pointsLengthOffset]; const addressing = Addressing.fromWire(this.indirect, operands.length); @@ -117,11 +117,5 @@ export class MultiScalarMul extends Instruction { memory.setSlice(outputOffset, [new Field(outputPoint.x), new Field(outputPoint.y)]); // Check representation of infinity for grumpkin memory.setSlice(outputOffset + 2, [new Uint1(outputPoint.equals(Point.ZERO) ? 1 : 0)]); - - memory.assert({ - reads: 1 + pointsReadLength + scalarReadLength /* points and scalars */, - writes: 3 /* output triplet */, - addressing, - }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/storage.ts b/yarn-project/simulator/src/avm/opcodes/storage.ts index 318e4d130932..38f8d77708ab 100644 --- a/yarn-project/simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/simulator/src/avm/opcodes/storage.ts @@ -32,7 +32,7 @@ export class SStore extends BaseStorageInstruction { throw new StaticCallAlterationError(); } - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.aOffset, this.bOffset]; @@ -44,8 +44,6 @@ export class SStore extends BaseStorageInstruction { const slot = memory.get(slotOffset).toFr(); const value = memory.get(srcOffset).toFr(); await context.persistableState.writeStorage(context.environment.address, slot, value); - - memory.assert({ reads: 2, addressing }); } } @@ -58,7 +56,7 @@ export class SLoad extends BaseStorageInstruction { } public async execute(context: AvmContext): Promise { - const memory = context.machineState.memory.track(this.type); + const memory = context.machineState.memory; context.machineState.consumeGas(this.gasCost()); const operands = [this.aOffset, this.bOffset]; @@ -69,7 +67,5 @@ export class SLoad extends BaseStorageInstruction { const slot = memory.get(slotOffset).toFr(); const value = await context.persistableState.readStorage(context.environment.address, slot); memory.set(dstOffset, new Field(value)); - - memory.assert({ writes: 1, reads: 1, addressing }); } }