diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index f590c6a40cf..4818e3dcee6 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -413,7 +413,7 @@ export class TaggedMemory implements TaggedMemoryInterface { } /** No-op. Implemented here for compatibility with the MeteredTaggedMemory. */ - public assert(_operations: Partial) {} + public assert(_operations: Partial) {} } /** Tagged memory wrapper with metering for each memory read and write operation. */ @@ -435,10 +435,15 @@ export class MeteredTaggedMemory implements TaggedMemoryInterface { * 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, indirect } = { reads: 0, writes: 0, ...operations }; - - const totalExpectedReads = expectedReads + Addressing.fromWire(indirect ?? 0).count(AddressingMode.INDIRECT); + 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( diff --git a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts index 44caeeaab8d..e7b16a6b4f0 100644 --- a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts @@ -28,13 +28,11 @@ export class NoteHashExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [noteHashOffset, leafIndexOffset, existsOffset] = Addressing.fromWire(this.indirect).resolve( - [this.noteHashOffset, this.leafIndexOffset, this.existsOffset], - memory, - ); + const operands = [this.noteHashOffset, this.leafIndexOffset, this.existsOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [noteHashOffset, leafIndexOffset, existsOffset] = addressing.resolve(operands, memory); memory.checkTags(TypeTag.FIELD, noteHashOffset, leafIndexOffset); // Note that this instruction accepts any type in memory, and converts to Field. @@ -48,7 +46,7 @@ export class NoteHashExists extends Instruction { ); memory.set(existsOffset, exists ? new Uint8(1) : new Uint8(0)); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -64,11 +62,12 @@ export class EmitNoteHash extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [noteHashOffset] = Addressing.fromWire(this.indirect).resolve([this.noteHashOffset], memory); + const operands = [this.noteHashOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [noteHashOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.FIELD, noteHashOffset); if (context.environment.isStaticCall) { @@ -78,7 +77,7 @@ export class EmitNoteHash extends Instruction { const noteHash = memory.get(noteHashOffset).toFr(); context.persistableState.writeNoteHash(context.environment.storageAddress, noteHash); - memory.assert(memoryOperations); + memory.assert({ reads: 1, addressing }); context.machineState.incrementPc(); } } @@ -105,14 +104,12 @@ export class NullifierExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [nullifierOffset, addressOffset, existsOffset] = Addressing.fromWire(this.indirect).resolve( - [this.nullifierOffset, this.addressOffset, this.existsOffset], - memory, - ); + const operands = [this.nullifierOffset, this.addressOffset, this.existsOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [nullifierOffset, addressOffset, existsOffset] = addressing.resolve(operands, memory); memory.checkTags(TypeTag.FIELD, nullifierOffset, addressOffset); const nullifier = memory.get(nullifierOffset).toFr(); @@ -121,7 +118,7 @@ export class NullifierExists extends Instruction { memory.set(existsOffset, exists ? new Uint8(1) : new Uint8(0)); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -141,11 +138,12 @@ export class EmitNullifier extends Instruction { throw new StaticCallAlterationError(); } - const memoryOperations = { reads: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [nullifierOffset] = Addressing.fromWire(this.indirect).resolve([this.nullifierOffset], memory); + const operands = [this.nullifierOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [nullifierOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.FIELD, nullifierOffset); const nullifier = memory.get(nullifierOffset).toFr(); @@ -162,7 +160,7 @@ export class EmitNullifier extends Instruction { } } - memory.assert(memoryOperations); + memory.assert({ reads: 1, addressing }); context.machineState.incrementPc(); } } @@ -189,14 +187,12 @@ export class L1ToL2MessageExists extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [msgHashOffset, msgLeafIndexOffset, existsOffset] = Addressing.fromWire(this.indirect).resolve( - [this.msgHashOffset, this.msgLeafIndexOffset, this.existsOffset], - memory, - ); + const operands = [this.msgHashOffset, this.msgLeafIndexOffset, this.existsOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [msgHashOffset, msgLeafIndexOffset, existsOffset] = addressing.resolve(operands, memory); memory.checkTags(TypeTag.FIELD, msgHashOffset, msgLeafIndexOffset); const msgHash = memory.get(msgHashOffset).toFr(); @@ -208,7 +204,7 @@ export class L1ToL2MessageExists extends Instruction { ); memory.set(existsOffset, exists ? new Uint8(1) : new Uint8(0)); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -230,22 +226,20 @@ export class EmitUnencryptedLog extends Instruction { const memory = context.machineState.memory.track(this.type); - const [logOffset, logSizeOffset] = Addressing.fromWire(this.indirect).resolve( - [this.logOffset, this.logSizeOffset], - memory, - ); + const operands = [this.logOffset, this.logSizeOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [logOffset, logSizeOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.UINT32, logSizeOffset); const logSize = memory.get(logSizeOffset).toNumber(); memory.checkTagsRange(TypeTag.FIELD, logOffset, logSize); const contractAddress = context.environment.address; - const memoryOperations = { reads: 1 + logSize, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(logSize)); const log = memory.getSlice(logOffset, logSize).map(f => f.toFr()); context.persistableState.writeUnencryptedLog(contractAddress, log); - memory.assert(memoryOperations); + memory.assert({ reads: 1 + logSize, addressing }); context.machineState.incrementPc(); } } @@ -265,20 +259,18 @@ export class SendL2ToL1Message extends Instruction { throw new StaticCallAlterationError(); } - const memoryOperations = { reads: 2, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [recipientOffset, contentOffset] = Addressing.fromWire(this.indirect).resolve( - [this.recipientOffset, this.contentOffset], - memory, - ); + const operands = [this.recipientOffset, this.contentOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [recipientOffset, contentOffset] = addressing.resolve(operands, memory); const recipient = memory.get(recipientOffset).toFr(); const content = memory.get(contentOffset).toFr(); context.persistableState.writeL2ToL1Message(recipient, content); - memory.assert(memoryOperations); + memory.assert({ reads: 2, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts index 911f297f784..2d054f7a299 100644 --- a/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts +++ b/yarn-project/simulator/src/avm/opcodes/addressing_mode.ts @@ -3,9 +3,10 @@ import { strict as assert } from 'assert'; import { type TaggedMemoryInterface } from '../avm_memory_types.js'; export enum AddressingMode { - DIRECT, - INDIRECT, - INDIRECT_PLUS_CONSTANT, // Not implemented yet. + DIRECT = 0, + INDIRECT = 1, + RELATIVE = 2, + INDIRECT_RELATIVE = 3, } /** A class to represent the addressing mode of an instruction. */ @@ -13,16 +14,17 @@ export class Addressing { public constructor( /** The addressing mode for each operand. The length of this array is the number of operands of the instruction. */ private readonly modePerOperand: AddressingMode[], - ) { - assert(modePerOperand.length <= 8, 'At most 8 operands are supported'); - } + ) {} - public static fromWire(wireModes: number): Addressing { + // TODO(facundo): 8 for backwards compatibility. + public static fromWire(wireModes: number, numOperands: number = 8): Addressing { // The modes are stored in the wire format as a byte, with each bit representing the mode for an operand. // The least significant bit represents the zeroth operand, and the most significant bit represents the last operand. - const modes = new Array(8); - for (let i = 0; i < 8; i++) { - modes[i] = (wireModes & (1 << i)) === 0 ? AddressingMode.DIRECT : AddressingMode.INDIRECT; + const modes = new Array(numOperands); + for (let i = 0; i < numOperands; i++) { + modes[i] = + (((wireModes >> i) & 1) * AddressingMode.INDIRECT) | + (((wireModes >> (i + numOperands)) & 1) * AddressingMode.RELATIVE); } return new Addressing(modes); } @@ -31,17 +33,20 @@ export class Addressing { // The modes are stored in the wire format as a byte, with each bit representing the mode for an operand. // The least significant bit represents the zeroth operand, and the least significant bit represents the last operand. let wire: number = 0; - for (let i = 0; i < 8; i++) { - if (this.modePerOperand[i] === AddressingMode.INDIRECT) { + for (let i = 0; i < this.modePerOperand.length; i++) { + if (this.modePerOperand[i] & AddressingMode.INDIRECT) { wire |= 1 << i; } + if (this.modePerOperand[i] & AddressingMode.RELATIVE) { + wire |= 1 << (this.modePerOperand.length + i); + } } return wire; } /** Returns how many operands use the given addressing mode. */ public count(mode: AddressingMode): number { - return this.modePerOperand.filter(m => m === mode).length; + return this.modePerOperand.filter(m => (m & mode) !== 0).length; } /** @@ -54,17 +59,15 @@ export class Addressing { assert(offsets.length <= this.modePerOperand.length); const resolved = new Array(offsets.length); for (const [i, offset] of offsets.entries()) { - switch (this.modePerOperand[i]) { - case AddressingMode.INDIRECT: - // NOTE(reviewer): less than equal is a deviation from the spec - i dont see why this shouldnt be possible! - mem.checkIsValidMemoryOffsetTag(offset); - resolved[i] = Number(mem.get(offset).toBigInt()); - break; - case AddressingMode.DIRECT: - resolved[i] = offset; - break; - default: - throw new Error(`Unimplemented addressing mode: ${AddressingMode[this.modePerOperand[i]]}`); + const mode = this.modePerOperand[i]; + resolved[i] = offset; + if (mode & AddressingMode.RELATIVE) { + mem.checkIsValidMemoryOffsetTag(0); + resolved[i] += Number(mem.get(0).toBigInt()); + } + if (mode & AddressingMode.INDIRECT) { + mem.checkIsValidMemoryOffsetTag(resolved[i]); + resolved[i] = Number(mem.get(resolved[i]).toBigInt()); } } return resolved; diff --git a/yarn-project/simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/simulator/src/avm/opcodes/arithmetic.test.ts index 3360317c3d6..79ee194be5d 100644 --- a/yarn-project/simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/arithmetic.test.ts @@ -2,6 +2,7 @@ import { type AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { initContext } from '../fixtures/index.js'; import { Opcode } from '../serialization/instruction_serialization.js'; +import { Addressing, AddressingMode } from './addressing_mode.js'; import { Add, Div, FieldDiv, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { @@ -54,6 +55,32 @@ describe('Arithmetic Instructions', () => { }); }); + it('Should add in relative indirect mode', async () => { + const a = new Field(1n); + const b = new Field(2n); + + context.machineState.memory.set(10, a); + context.machineState.memory.set(11, b); + + context.machineState.memory.set(0, new Uint32(30)); // stack pointer + context.machineState.memory.set(32, new Uint32(5)); // indirect + + await new Add( + /*indirect=*/ new Addressing([ + /*aOffset*/ AddressingMode.DIRECT, + /*bOffset*/ AddressingMode.DIRECT, + /*dstOffset*/ AddressingMode.INDIRECT | AddressingMode.RELATIVE, + ]).toWire(), + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 10, + /*bOffset=*/ 11, + /*dstOffset=*/ 2, // We expect the result to be stored at MEM[30 + 2] = 5 + ).execute(context); + + const actual = context.machineState.memory.get(5); + expect(actual).toEqual(new Field(3n)); + }); + describe.each([ [new Field((Field.MODULUS + 1n) / 2n), new Field(1n), TypeTag.FIELD], [new Uint8((1n << 7n) + 1n), new Uint8(2n), TypeTag.UINT8], diff --git a/yarn-project/simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/simulator/src/avm/opcodes/arithmetic.ts index 24bbf6763bb..6aed60df9bf 100644 --- a/yarn-project/simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/simulator/src/avm/opcodes/arithmetic.ts @@ -6,14 +6,12 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [aOffset, bOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.aOffset, this.bOffset, this.dstOffset], - memory, - ); + const operands = [this.aOffset, this.bOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory); memory.checkTags(this.inTag, aOffset, bOffset); const a = memory.get(aOffset); @@ -22,7 +20,7 @@ export abstract class ThreeOperandArithmeticInstruction extends ThreeOperandInst const dest = this.compute(a, b); memory.set(dstOffset, dest); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } diff --git a/yarn-project/simulator/src/avm/opcodes/bitwise.ts b/yarn-project/simulator/src/avm/opcodes/bitwise.ts index b509d285c50..72592ed9f2f 100644 --- a/yarn-project/simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/simulator/src/avm/opcodes/bitwise.ts @@ -7,14 +7,12 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [aOffset, bOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.aOffset, this.bOffset, this.dstOffset], - memory, - ); + const operands = [this.aOffset, this.bOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory); this.checkTags(memory, this.inTag, aOffset, bOffset); const a = memory.getAs(aOffset); @@ -23,7 +21,7 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction { const res = this.compute(a, b); memory.set(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } @@ -98,18 +96,19 @@ export class Not extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.srcOffset, this.dstOffset], memory); + const operands = [this.srcOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [srcOffset, dstOffset] = addressing.resolve(operands, memory); TaggedMemory.checkIsIntegralTag(memory.getTag(srcOffset)); const value = memory.getAs(srcOffset); const res = value.not(); memory.set(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: 1, writes: 1, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/commitment.ts b/yarn-project/simulator/src/avm/opcodes/commitment.ts index cb1ebf84d21..a98a21abec2 100644 --- a/yarn-project/simulator/src/avm/opcodes/commitment.ts +++ b/yarn-project/simulator/src/avm/opcodes/commitment.ts @@ -32,10 +32,10 @@ export class PedersenCommitment extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [inputOffset, outputOffset, inputSizeOffset, genIndexOffset] = Addressing.fromWire(this.indirect).resolve( - [this.inputOffset, this.outputOffset, this.inputSizeOffset, this.genIndexOffset], - memory, - ); + + const operands = [this.inputOffset, this.outputOffset, this.inputSizeOffset, this.genIndexOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [inputOffset, outputOffset, inputSizeOffset, genIndexOffset] = addressing.resolve(operands, memory); const inputSize = memory.get(inputSizeOffset).toNumber(); memory.checkTag(TypeTag.UINT32, inputSizeOffset); @@ -46,7 +46,6 @@ export class PedersenCommitment extends Instruction { const generatorIndex = memory.get(genIndexOffset).toNumber(); memory.checkTag(TypeTag.UINT32, genIndexOffset); - const memoryOperations = { reads: inputSize + 2, writes: 3, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(inputSize)); const inputBuffer: Buffer[] = inputs.map(input => input.toBuffer()); @@ -60,7 +59,7 @@ export class PedersenCommitment extends Instruction { memory.set(outputOffset + 1, commitment[1]); // Field typed memory.set(outputOffset + 2, new Uint8(isInfinity ? 1 : 0)); // U8 typed - memory.assert(memoryOperations); + memory.assert({ reads: inputSize + 2, writes: 3, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.ts b/yarn-project/simulator/src/avm/opcodes/comparators.ts index 70e95da2bea..d7d12d099d7 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.ts @@ -6,14 +6,12 @@ import { ThreeOperandInstruction } from './instruction_impl.js'; abstract class ComparatorInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 2, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [aOffset, bOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.aOffset, this.bOffset, this.dstOffset], - memory, - ); + const operands = [this.aOffset, this.bOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory); memory.checkTags(this.inTag, aOffset, bOffset); const a = memory.get(aOffset); @@ -22,7 +20,7 @@ abstract class ComparatorInstruction extends ThreeOperandInstruction { const dest = new Uint1(this.compare(a, b) ? 1 : 0); memory.set(dstOffset, dest); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: 1, addressing }); context.machineState.incrementPc(); } diff --git a/yarn-project/simulator/src/avm/opcodes/contract.ts b/yarn-project/simulator/src/avm/opcodes/contract.ts index baa793a037e..e8c7c743479 100644 --- a/yarn-project/simulator/src/avm/opcodes/contract.ts +++ b/yarn-project/simulator/src/avm/opcodes/contract.ts @@ -22,14 +22,12 @@ export class GetContractInstance extends Instruction { } async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, writes: 6, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [addressOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.addressOffset, this.dstOffset], - memory, - ); + const operands = [this.addressOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [addressOffset, dstOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.FIELD, addressOffset); const address = memory.get(addressOffset).toFr(); @@ -46,7 +44,7 @@ export class GetContractInstance extends Instruction { memory.setSlice(dstOffset, data); - memory.assert(memoryOperations); + memory.assert({ reads: 1, writes: 6, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/control_flow.ts b/yarn-project/simulator/src/avm/opcodes/control_flow.ts index f668b966f50..9109682a6a5 100644 --- a/yarn-project/simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/simulator/src/avm/opcodes/control_flow.ts @@ -41,21 +41,21 @@ export class JumpI extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [condOffset] = Addressing.fromWire(this.indirect).resolve([this.condOffset], memory); + const operands = [this.condOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [condOffset] = addressing.resolve(operands, memory); const condition = memory.getAs(condOffset); - // TODO: reconsider this casting if (condition.toBigInt() == 0n) { context.machineState.incrementPc(); } else { context.machineState.pc = this.loc; } - memory.assert(memoryOperations); + memory.assert({ reads: 1, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/conversion.ts b/yarn-project/simulator/src/avm/opcodes/conversion.ts index 770bfc08a3b..893b09a7942 100644 --- a/yarn-project/simulator/src/avm/opcodes/conversion.ts +++ b/yarn-project/simulator/src/avm/opcodes/conversion.ts @@ -33,11 +33,9 @@ export class ToRadixLE extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [srcOffset, dstOffset, radixOffset] = Addressing.fromWire(this.indirect).resolve( - [this.srcOffset, this.dstOffset, this.radixOffset], - memory, - ); - const memoryOperations = { reads: 2, writes: this.numLimbs, indirect: this.indirect }; + const operands = [this.srcOffset, this.dstOffset, this.radixOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [srcOffset, dstOffset, radixOffset] = addressing.resolve(operands, memory); context.machineState.consumeGas(this.gasCost(this.numLimbs)); // The radix gadget only takes in a Field @@ -62,7 +60,7 @@ export class ToRadixLE extends Instruction { const res = limbArray.map(byte => new outputType(byte)); memory.setSlice(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: this.numLimbs, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/ec_add.ts b/yarn-project/simulator/src/avm/opcodes/ec_add.ts index f02f62163dd..7f4219ea395 100644 --- a/yarn-project/simulator/src/avm/opcodes/ec_add.ts +++ b/yarn-project/simulator/src/avm/opcodes/ec_add.ts @@ -38,23 +38,21 @@ export class EcAdd extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 6, writes: 3, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); + const operands = [ + this.p1XOffset, + this.p1YOffset, + this.p1IsInfiniteOffset, + this.p2XOffset, + this.p2YOffset, + this.p2IsInfiniteOffset, + this.dstOffset, + ]; + const addressing = Addressing.fromWire(this.indirect, operands.length); const [p1XOffset, p1YOffset, p1IsInfiniteOffset, p2XOffset, p2YOffset, p2IsInfiniteOffset, dstOffset] = - Addressing.fromWire(this.indirect).resolve( - [ - this.p1XOffset, - this.p1YOffset, - this.p1IsInfiniteOffset, - this.p2XOffset, - this.p2YOffset, - this.p2IsInfiniteOffset, - this.dstOffset, - ], - memory, - ); + addressing.resolve(operands, memory); const p1X = memory.get(p1XOffset); const p1Y = memory.get(p1YOffset); @@ -86,7 +84,7 @@ export class EcAdd extends Instruction { // Check representation of infinity for grumpkin memory.set(dstOffset + 2, new Field(dest.equals(Point.ZERO) ? 1 : 0)); - memory.assert(memoryOperations); + memory.assert({ reads: 6, writes: 3, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts index a91a174b633..1d47b6986aa 100644 --- a/yarn-project/simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/simulator/src/avm/opcodes/environment_getters.ts @@ -71,15 +71,16 @@ export class GetEnvVar extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [dstOffset] = Addressing.fromWire(this.indirect).resolve([this.dstOffset], memory); + const operands = [this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [dstOffset] = addressing.resolve(operands, memory); memory.set(dstOffset, getValue(this.varEnum, context)); - memory.assert(memoryOperations); + memory.assert({ writes: 1, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.ts index 13a15efa2e4..67c7d2f565d 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.ts @@ -45,10 +45,17 @@ abstract class ExternalCall extends Instruction { public async execute(context: AvmContext) { const memory = context.machineState.memory.track(this.type); - const [gasOffset, addrOffset, argsOffset, argsSizeOffset, retOffset, successOffset] = Addressing.fromWire( - this.indirect, - ).resolve( - [this.gasOffset, this.addrOffset, this.argsOffset, this.argsSizeOffset, this.retOffset, this.successOffset], + const operands = [ + this.gasOffset, + this.addrOffset, + this.argsOffset, + this.argsSizeOffset, + this.retOffset, + this.successOffset, + ]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [gasOffset, addrOffset, argsOffset, argsSizeOffset, retOffset, successOffset] = addressing.resolve( + operands, memory, ); memory.checkTags(TypeTag.FIELD, gasOffset, gasOffset + 1); @@ -66,7 +73,6 @@ abstract class ExternalCall extends Instruction { const callType = context.environment.isStaticCall ? 'STATICCALL' : this.type; // First we consume the gas for this operation. - const memoryOperations = { reads: calldataSize + 5, writes: 1 + this.retSize, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(calldataSize + this.retSize)); // Then we consume the gas allocated for the nested call. The excess will be refunded later. // Gas allocation is capped by the amount of gas left in the current context. @@ -127,7 +133,7 @@ abstract class ExternalCall extends Instruction { /*avmCallResults=*/ nestedCallResults, ); - memory.assert(memoryOperations); + memory.assert({ reads: calldataSize + 5, writes: 1 + this.retSize, addressing }); context.machineState.incrementPc(); } @@ -168,16 +174,17 @@ export class Return extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: this.copySize, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost(this.copySize)); - const [returnOffset] = Addressing.fromWire(this.indirect).resolve([this.returnOffset], memory); + const operands = [this.returnOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [returnOffset] = addressing.resolve(operands, memory); const output = memory.getSlice(returnOffset, this.copySize).map(word => word.toFr()); context.machineState.return(output); - memory.assert(memoryOperations); + memory.assert({ reads: this.copySize, addressing }); } } @@ -203,16 +210,17 @@ export class Revert extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: this.retSize, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost(this.retSize)); - const [returnOffset] = Addressing.fromWire(this.indirect).resolve([this.returnOffset], memory); + const operands = [this.returnOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [returnOffset] = addressing.resolve(operands, memory); const output = memory.getSlice(returnOffset, this.retSize).map(word => word.toFr()); context.machineState.revert(output); - memory.assert(memoryOperations); + memory.assert({ reads: this.retSize, addressing }); } } diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index ce5fb7cccb3..5b34d868bc8 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -33,14 +33,12 @@ export class Poseidon2 extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: Poseidon2.stateSize, writes: Poseidon2.stateSize, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [inputOffset, outputOffset] = Addressing.fromWire(this.indirect).resolve( - [this.inputStateOffset, this.outputStateOffset], - memory, - ); + const operands = [this.inputStateOffset, this.outputStateOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [inputOffset, outputOffset] = addressing.resolve(operands, memory); memory.checkTagsRange(TypeTag.FIELD, inputOffset, Poseidon2.stateSize); const inputState = memory.getSlice(inputOffset, Poseidon2.stateSize); @@ -50,7 +48,7 @@ export class Poseidon2 extends Instruction { outputState.map(word => new Field(word)), ); - memory.assert(memoryOperations); + memory.assert({ reads: Poseidon2.stateSize, writes: Poseidon2.stateSize, addressing }); context.machineState.incrementPc(); } } @@ -80,13 +78,11 @@ export class Keccak extends Instruction { // pub fn keccak256(input: [u8], message_size: u32) -> [u8; 32] public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [dstOffset, messageOffset, messageSizeOffset] = Addressing.fromWire(this.indirect).resolve( - [this.dstOffset, this.messageOffset, this.messageSizeOffset], - memory, - ); + const operands = [this.dstOffset, this.messageOffset, this.messageSizeOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [dstOffset, messageOffset, messageSizeOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.UINT32, messageSizeOffset); const messageSize = memory.get(messageSizeOffset).toNumber(); - const memoryOperations = { reads: messageSize + 1, writes: 32, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(messageSize)); memory.checkTagsRange(TypeTag.UINT8, messageOffset, messageSize); @@ -98,7 +94,7 @@ export class Keccak extends Instruction { const res = [...hashBuffer].map(byte => new Uint8(byte)); memory.setSlice(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: messageSize + 1, writes: 32, addressing }); context.machineState.incrementPc(); } } @@ -129,14 +125,12 @@ export class KeccakF1600 extends Instruction { // pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [dstOffset, stateOffset, stateSizeOffset] = Addressing.fromWire(this.indirect).resolve( - [this.dstOffset, this.stateOffset, this.stateSizeOffset], - memory, - ); + const operands = [this.dstOffset, this.stateOffset, this.stateSizeOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [dstOffset, stateOffset, stateSizeOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.UINT32, stateSizeOffset); const stateSize = memory.get(stateSizeOffset).toNumber(); assert(stateSize === 25, 'Invalid state size for keccakf1600'); - const memoryOperations = { reads: stateSize + 1, writes: 25, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost()); memory.checkTagsRange(TypeTag.UINT64, stateOffset, stateSize); @@ -147,7 +141,7 @@ export class KeccakF1600 extends Instruction { const res = updatedState.map(word => new Uint64(word)); memory.setSlice(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: stateSize + 1, writes: 25, addressing }); context.machineState.incrementPc(); } } @@ -183,10 +177,16 @@ export class Sha256Compression extends Instruction { const INPUTS_SIZE = 16; const memory = context.machineState.memory.track(this.type); - const [outputOffset, stateOffset, stateSizeOffset, inputsOffset, inputsSizeOffset] = Addressing.fromWire( - this.indirect, - ).resolve( - [this.outputOffset, this.stateOffset, this.stateSizeOffset, this.inputsOffset, this.inputsSizeOffset], + const operands = [ + this.outputOffset, + this.stateOffset, + this.stateSizeOffset, + this.inputsOffset, + this.inputsSizeOffset, + ]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [outputOffset, stateOffset, stateSizeOffset, inputsOffset, inputsSizeOffset] = addressing.resolve( + operands, memory, ); const stateSize = memory.get(stateSizeOffset).toNumber(); @@ -199,7 +199,6 @@ export class Sha256Compression extends Instruction { } // +2 to account for both size offsets (stateSizeOffset and inputsSizeOffset) // Note: size of output is same as size of state - const memoryOperations = { reads: stateSize + inputsSize + 2, writes: stateSize, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost()); memory.checkTagsRange(TypeTag.UINT32, inputsOffset, inputsSize); memory.checkTagsRange(TypeTag.UINT32, stateOffset, stateSize); @@ -212,7 +211,7 @@ export class Sha256Compression extends Instruction { const res = [...output].map(word => new Uint32(word)); memory.setSlice(outputOffset, res); - memory.assert(memoryOperations); + memory.assert({ reads: stateSize + inputsSize + 2, writes: stateSize, addressing }); context.machineState.incrementPc(); } } @@ -243,10 +242,9 @@ export class Pedersen extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [genIndexOffset, dstOffset, messageOffset, messageSizeOffset] = Addressing.fromWire(this.indirect).resolve( - [this.genIndexOffset, this.dstOffset, this.messageOffset, this.messageSizeOffset], - memory, - ); + const operands = [this.genIndexOffset, this.dstOffset, this.messageOffset, this.messageSizeOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [genIndexOffset, dstOffset, messageOffset, messageSizeOffset] = addressing.resolve(operands, memory); // We hash a set of field elements const genIndex = Number(memory.get(genIndexOffset).toBigInt()); @@ -255,7 +253,6 @@ export class Pedersen extends Instruction { memory.checkTag(TypeTag.UINT32, messageSizeOffset); const hashData = memory.getSlice(messageOffset, messageSize); - const memoryOperations = { reads: messageSize + 2, writes: 1, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(messageSize)); memory.checkTagsRange(TypeTag.FIELD, messageOffset, messageSize); @@ -264,7 +261,7 @@ export class Pedersen extends Instruction { const hash = pedersenHash(hashData, genIndex); memory.set(dstOffset, new Field(hash)); - memory.assert(memoryOperations); + memory.assert({ reads: messageSize + 2, writes: 1, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/memory.ts b/yarn-project/simulator/src/avm/opcodes/memory.ts index a41bd0b71ca..605f97eccf0 100644 --- a/yarn-project/simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/simulator/src/avm/opcodes/memory.ts @@ -62,15 +62,16 @@ export class Set extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [dstOffset] = Addressing.fromWire(this.indirect).resolve([this.dstOffset], memory); + const operands = [this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [dstOffset] = addressing.resolve(operands, memory); const res = TaggedMemory.buildFromTagTruncating(this.value, this.inTag); memory.set(dstOffset, res); - memory.assert(memoryOperations); + memory.assert({ writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -99,14 +100,12 @@ export class CMov extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 3, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [aOffset, bOffset, condOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.aOffset, this.bOffset, this.condOffset, this.dstOffset], - memory, - ); + const operands = [this.aOffset, this.bOffset, this.condOffset, this.dstOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [aOffset, bOffset, condOffset, dstOffset] = addressing.resolve(operands, memory); const a = memory.get(aOffset); const b = memory.get(bOffset); @@ -115,7 +114,7 @@ export class CMov extends Instruction { // TODO: reconsider toBigInt() here memory.set(dstOffset, cond.toBigInt() > 0 ? a : b); - memory.assert(memoryOperations); + memory.assert({ reads: 3, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -144,18 +143,19 @@ export class Cast extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.srcOffset, this.dstOffset], memory); + 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); const casted = TaggedMemory.buildFromTagTruncating(a.toBigInt(), this.dstTag); memory.set(dstOffset, casted); - memory.assert(memoryOperations); + memory.assert({ reads: 1, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -183,17 +183,18 @@ export class Mov extends Instruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { reads: 1, writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.srcOffset, this.dstOffset], memory); + 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(memoryOperations); + memory.assert({ reads: 1, writes: 1, addressing }); context.machineState.incrementPc(); } } @@ -222,21 +223,19 @@ export class CalldataCopy extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); // We don't need to check tags here because: (1) the calldata is NOT in memory, and (2) we are the ones writing to destination. - const [cdStartOffset, copySizeOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( - [this.cdStartOffset, this.copySizeOffset, this.dstOffset], - 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); const cdStart = memory.get(cdStartOffset).toNumber(); const copySize = memory.get(copySizeOffset).toNumber(); - const memoryOperations = { reads: 2, writes: copySize, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost(copySize)); const transformedData = context.environment.calldata.slice(cdStart, cdStart + copySize).map(f => new Field(f)); memory.setSlice(dstOffset, transformedData); - memory.assert(memoryOperations); + memory.assert({ reads: 2, writes: copySize, addressing }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/misc.ts b/yarn-project/simulator/src/avm/opcodes/misc.ts index dd4ca68d1e6..219145778d7 100644 --- a/yarn-project/simulator/src/avm/opcodes/misc.ts +++ b/yarn-project/simulator/src/avm/opcodes/misc.ts @@ -33,16 +33,14 @@ export class DebugLog extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); - const [messageOffset, fieldsOffset, fieldsSizeOffset] = Addressing.fromWire(this.indirect).resolve( - [this.messageOffset, this.fieldsOffset, this.fieldsSizeOffset], - 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); const fieldsSize = memory.get(fieldsSizeOffset).toNumber(); memory.checkTagsRange(TypeTag.UINT8, messageOffset, this.messageSize); memory.checkTagsRange(TypeTag.FIELD, fieldsOffset, fieldsSize); - const memoryOperations = { reads: 1 + fieldsSize + this.messageSize, writes: 0, indirect: this.indirect }; context.machineState.consumeGas(this.gasCost()); const rawMessage = memory.getSlice(messageOffset, this.messageSize); @@ -57,7 +55,7 @@ export class DebugLog extends Instruction { DebugLog.logger.verbose(formattedStr); - memory.assert(memoryOperations); + memory.assert({ reads: 1 + fieldsSize + this.messageSize, addressing }); context.machineState.incrementPc(); } } 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 cb4b8e263d5..7a7b149fbf5 100644 --- a/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts +++ b/yarn-project/simulator/src/avm/opcodes/multi_scalar_mul.ts @@ -35,10 +35,9 @@ export class MultiScalarMul extends Instruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory.track(this.type); // Resolve indirects - const [pointsOffset, scalarsOffset, outputOffset] = Addressing.fromWire(this.indirect).resolve( - [this.pointsOffset, this.scalarsOffset, this.outputOffset], - memory, - ); + const operands = [this.pointsOffset, this.scalarsOffset, this.outputOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [pointsOffset, scalarsOffset, outputOffset] = addressing.resolve(operands, memory); // Length of the points vector should be U32 memory.checkTag(TypeTag.UINT32, this.pointsLengthOffset); @@ -62,12 +61,6 @@ export class MultiScalarMul extends Instruction { // The size of the scalars vector is twice the NUMBER of points because of the scalar limb decomposition const scalarReadLength = numPoints * 2; - // Consume gas prior to performing work - const memoryOperations = { - reads: 1 + pointsReadLength + scalarReadLength /* points and scalars */, - writes: 3 /* output triplet */, - indirect: this.indirect, - }; context.machineState.consumeGas(this.gasCost(pointsReadLength)); // Get the unrolled scalar (lo & hi) representing the scalars const scalarsVector = memory.getSlice(scalarsOffset, scalarReadLength); @@ -108,7 +101,11 @@ export class MultiScalarMul extends Instruction { memory.setSlice(outputOffset, output); - memory.assert(memoryOperations); + memory.assert({ + reads: 1 + pointsReadLength + scalarReadLength /* points and scalars */, + writes: 3 /* output triplet */, + addressing, + }); context.machineState.incrementPc(); } } diff --git a/yarn-project/simulator/src/avm/opcodes/storage.ts b/yarn-project/simulator/src/avm/opcodes/storage.ts index 553737d11bb..4da3a619ab9 100644 --- a/yarn-project/simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/simulator/src/avm/opcodes/storage.ts @@ -32,11 +32,12 @@ export class SStore extends BaseStorageInstruction { throw new StaticCallAlterationError(); } - const memoryOperations = { reads: 2, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [srcOffset, slotOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.bOffset], memory); + const operands = [this.aOffset, this.bOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [srcOffset, slotOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.FIELD, slotOffset); memory.checkTag(TypeTag.FIELD, srcOffset); @@ -44,7 +45,7 @@ export class SStore extends BaseStorageInstruction { const value = memory.get(srcOffset).toFr(); context.persistableState.writeStorage(context.environment.storageAddress, slot, value); - memory.assert(memoryOperations); + memory.assert({ reads: 2, addressing }); context.machineState.incrementPc(); } } @@ -58,11 +59,12 @@ export class SLoad extends BaseStorageInstruction { } public async execute(context: AvmContext): Promise { - const memoryOperations = { writes: 1, reads: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost()); - const [slotOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.bOffset], memory); + const operands = [this.aOffset, this.bOffset]; + const addressing = Addressing.fromWire(this.indirect, operands.length); + const [slotOffset, dstOffset] = addressing.resolve(operands, memory); memory.checkTag(TypeTag.FIELD, slotOffset); const slot = memory.get(slotOffset).toFr(); @@ -70,6 +72,6 @@ export class SLoad extends BaseStorageInstruction { memory.set(dstOffset, new Field(value)); context.machineState.incrementPc(); - memory.assert(memoryOperations); + memory.assert({ writes: 1, reads: 1, addressing }); } }