diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index aa0fc689b58..53e8599b7c3 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -12,11 +12,22 @@ export class AvmMachineState { /** - */ public memory: Fr[]; + /** + * When an internal_call is invoked, the internal call stack is added to with the current pc + 1 + * When internal_return is invoked, the latest value is popped from the internal call stack and set to the pc. + */ + public internalCallStack: number[]; + /** - */ public pc: number; /** - */ public callStack: number[]; + /** + * If an instruction triggers a halt, then it ends execution of the VM + */ + public halted: boolean; + /** * Create a new avm context * @param calldata - @@ -25,9 +36,12 @@ export class AvmMachineState { this.calldata = calldata; this.returnData = []; this.memory = []; + this.internalCallStack = []; this.pc = 0; this.callStack = []; + + this.halted = false; } /** diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts index 19fb0e99b54..a52a2c13222 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts @@ -5,7 +5,7 @@ import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Add } from '../opcodes/arithmetic.js'; -import { Return } from '../opcodes/control_flow.js'; +import { Jump, Return } from '../opcodes/control_flow.js'; import { Instruction } from '../opcodes/instruction.js'; import { CalldataCopy } from '../opcodes/memory.js'; import { AvmInterpreter } from './interpreter.js'; @@ -34,4 +34,20 @@ describe('interpreter', () => { expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); + + it('Should revert with an invalid jump', () => { + const calldata: Fr[] = []; + const stateManager = mock(); + + const invalidJumpDestination = 22; + + const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; + + const context = new AvmMachineState(calldata); + const interpreter = new AvmInterpreter(context, stateManager, instructions); + + const avmReturnData = interpreter.run(); + + expect(avmReturnData.reverted).toBe(true); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts index 47fb9c3771d..9bbd511c2d3 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts @@ -30,8 +30,18 @@ export class AvmInterpreter { */ run(): AvmMessageCallResult { try { - for (const instruction of this.instructions) { + while (!this.machineState.halted && this.machineState.pc < this.instructions.length) { + const instruction = this.instructions[this.machineState.pc]; + + if (!instruction) { + throw new InvalidInstructionError(this.machineState.pc); + } + instruction.execute(this.machineState, this.stateManager); + + if (this.machineState.pc >= this.instructions.length) { + throw new InvalidProgramCounterError(this.machineState.pc, this.instructions.length); + } } const returnData = this.machineState.getReturnData(); @@ -52,3 +62,22 @@ export class AvmInterpreter { return this.machineState.getReturnData(); } } + +/** + * Error is thrown when the program counter goes to an invalid location. + * There is no instruction at the provided pc + */ +class InvalidProgramCounterError extends Error { + constructor(pc: number, max: number) { + super(`Invalid program counter ${pc}, max is ${max}`); + } +} + +/** + * This assertion should never be hit - there should always be a valid instruction + */ +class InvalidInstructionError extends Error { + constructor(pc: number) { + super(`Invalid instruction at ${pc}`); + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index ab6465debf4..7f2fb3db052 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -5,43 +5,53 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; /** -*/ -export class Add implements Instruction { +export class Add extends Instruction { static type: string = 'ADD'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a = machineState.readMemory(this.aOffset); const b = machineState.readMemory(this.bOffset); - const dest = new Fr(a.toBigInt() + (b.toBigInt() % Fr.MODULUS)); + const dest = new Fr((a.toBigInt() + b.toBigInt()) % Fr.MODULUS); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Sub implements Instruction { +export class Sub extends Instruction { static type: string = 'SUB'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a = machineState.readMemory(this.aOffset); const b = machineState.readMemory(this.bOffset); - const dest = new Fr(a.toBigInt() - (b.toBigInt() % Fr.MODULUS)); + const dest = new Fr((a.toBigInt() - b.toBigInt()) % Fr.MODULUS); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Mul implements Instruction { +export class Mul extends Instruction { static type: string = 'MUL'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -49,15 +59,19 @@ export class Mul implements Instruction { const dest = new Fr((a.toBigInt() * b.toBigInt()) % Fr.MODULUS); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Div implements Instruction { +export class Div extends Instruction { static type: string = 'DIV'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -66,5 +80,7 @@ export class Div implements Instruction { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3993): proper field division const dest = new Fr(a.toBigInt() / b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 62376269d8e..bc5c3a211cb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -5,11 +5,13 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; /** - */ -export class And implements Instruction { +export class And extends Instruction { static type: string = 'AND'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -17,15 +19,19 @@ export class And implements Instruction { const dest = new Fr(a.toBigInt() & b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** - */ -export class Or implements Instruction { +export class Or extends Instruction { static type: string = 'OR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -33,15 +39,19 @@ export class Or implements Instruction { const dest = new Fr(a.toBigInt() | b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** - */ -export class Xor implements Instruction { +export class Xor extends Instruction { static type: string = 'XOR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -49,30 +59,40 @@ export class Xor implements Instruction { const dest = new Fr(a.toBigInt() ^ b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** - */ -export class Not implements Instruction { +export class Not extends Instruction { static type: string = 'NOT'; static numberOfOperands = 2; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); - const dest = new Fr(~a.toBigInt()); + // TODO: hack -> until proper field arithmetic is implemented + const result = ~a.toBigInt(); + const dest = new Fr(result < 0 ? Fr.MODULUS + /* using a + as result is -ve*/ result : result); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Shl implements Instruction { +export class Shl extends Instruction { static type: string = 'SHL'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -80,15 +100,19 @@ export class Shl implements Instruction { const dest = new Fr(a.toBigInt() << b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Shr implements Instruction { +export class Shr extends Instruction { static type: string = 'SHR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -96,5 +120,7 @@ export class Shr implements Instruction { const dest = new Fr(a.toBigInt() >> b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index ddd3c9d1de9..685a5438c42 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -5,11 +5,13 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; /** -*/ -export class Eq implements Instruction { +export class Eq extends Instruction { static type: string = 'EQ'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -17,14 +19,18 @@ export class Eq implements Instruction { const dest = new Fr(a.toBigInt() == b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Lt implements Instruction { +export class Lt extends Instruction { static type: string = 'Lt'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -32,15 +38,19 @@ export class Lt implements Instruction { const dest = new Fr(a.toBigInt() < b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } /** -*/ -export class Lte implements Instruction { +export class Lte extends Instruction { static type: string = 'LTE'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a: Fr = machineState.readMemory(this.aOffset); @@ -48,5 +58,7 @@ export class Lte implements Instruction { const dest = new Fr(a.toBigInt() < b.toBigInt()); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts new file mode 100644 index 00000000000..864ee0d0b42 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -0,0 +1,146 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmStateManager } from '../avm_state_manager.js'; +import { Add, Mul, Sub } from './arithmetic.js'; +import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; +import { Eq, Lt, Lte } from './comparators.js'; +import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js'; +import { CalldataCopy, Cast, Mov, Set } from './memory.js'; + +describe('Control Flow Opcodes', () => { + let stateManager = mock(); + let machineState: AvmMachineState; + + beforeEach(() => { + stateManager = mock(); + machineState = new AvmMachineState([]); + }); + + it('Should implement JUMP', () => { + const jumpLocation = 22; + + expect(machineState.pc).toBe(0); + + const instruction = new Jump(jumpLocation); + instruction.execute(machineState, stateManager); + expect(machineState.pc).toBe(jumpLocation); + }); + + it('Should implement JUMPI - truthy', () => { + const jumpLocation = 22; + const jumpLocation1 = 69; + + expect(machineState.pc).toBe(0); + + machineState.writeMemory(0, new Fr(1n)); + machineState.writeMemory(1, new Fr(2n)); + + const instruction = new JumpI(jumpLocation, 0); + instruction.execute(machineState, stateManager); + expect(machineState.pc).toBe(jumpLocation); + + // Truthy can be greater than 1 + const instruction1 = new JumpI(jumpLocation1, 1); + instruction1.execute(machineState, stateManager); + expect(machineState.pc).toBe(jumpLocation1); + }); + + it('Should implement JUMPI - falsy', () => { + const jumpLocation = 22; + + expect(machineState.pc).toBe(0); + + machineState.writeMemory(0, new Fr(0n)); + + const instruction = new JumpI(jumpLocation, 0); + instruction.execute(machineState, stateManager); + expect(machineState.pc).toBe(1); + }); + + it('Should implement Internal Call and Return', () => { + const jumpLocation = 22; + + expect(machineState.pc).toBe(0); + + const instruction = new InternalCall(jumpLocation); + const returnInstruction = new InternalReturn(); + + instruction.execute(machineState, stateManager); + expect(machineState.pc).toBe(jumpLocation); + + returnInstruction.execute(machineState, stateManager); + expect(machineState.pc).toBe(1); + }); + + it('Should chain series of control flow instructions', () => { + const jumpLocation0 = 22; + const jumpLocation1 = 69; + const jumpLocation2 = 1337; + + const aloneJumpLocation = 420; + + const instructions = [ + // pc | internal call stack + new InternalCall(jumpLocation0), // 22 | [1] + new InternalCall(jumpLocation1), // 69 | [1, 23] + new InternalReturn(), // 23 | [1] + new Jump(aloneJumpLocation), // 420 | [1] + new InternalCall(jumpLocation2), // 1337| [1, 421] + new InternalReturn(), // 421 | [1] + new InternalReturn(), // 1 | [] + ]; + + // The expected program counter after each instruction is invoked + const expectedPcs = [ + jumpLocation0, + jumpLocation1, + jumpLocation0 + 1, + aloneJumpLocation, + jumpLocation2, + aloneJumpLocation + 1, + 1, + ]; + + for (let i = 0; i < instructions.length; i++) { + instructions[i].execute(machineState, stateManager); + expect(machineState.pc).toBe(expectedPcs[i]); + } + }); + + it('Should error if Internal Return is called without a corresponding Internal Call', () => { + const returnInstruction = new InternalReturn(); + expect(() => returnInstruction.execute(machineState, stateManager)).toThrow(InternalCallStackEmptyError); + }); + + it('Should increment PC on All other Instructions', () => { + const instructions = [ + new Add(0, 1, 2), + new Sub(0, 1, 2), + new Mul(0, 1, 2), + new Lt(0, 1, 2), + new Lte(0, 1, 2), + new Eq(0, 1, 2), + new Xor(0, 1, 2), + new And(0, 1, 2), + new Or(0, 1, 2), + new Shl(0, 1, 2), + new Shr(0, 1, 2), + new Not(0, 2), + new CalldataCopy(0, 1, 2), + new Set(0n, 1), + new Mov(0, 1), + new Cast(0, 1), + ]; + + for (const instruction of instructions) { + // Use a fresh machine state each run + const innerMachineState = new AvmMachineState([]); + expect(machineState.pc).toBe(0); + instruction.execute(innerMachineState, stateManager); + expect(innerMachineState.pc).toBe(1); + } + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 31336e397be..b988979076e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -3,14 +3,94 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; /** - */ -export class Return implements Instruction { +export class Return extends Instruction { static type: string = 'RETURN'; static numberOfOperands = 2; - constructor(private returnOffset: number, private copySize: number) {} + constructor(private returnOffset: number, private copySize: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const returnData = machineState.readMemoryChunk(this.returnOffset, this.returnOffset + this.copySize); machineState.setReturnData(returnData); + + this.halt(machineState); + } +} + +/** -*/ +export class Jump extends Instruction { + static type: string = 'JUMP'; + static numberOfOperands = 1; + + constructor(private jumpOffset: number) { + super(); + } + + execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + machineState.pc = this.jumpOffset; + } +} + +/** -*/ +export class JumpI extends Instruction { + static type: string = 'JUMPI'; + static numberOfOperands = 1; + + constructor(private jumpOffset: number, private condOffset: number) { + super(); + } + + execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + const condition = machineState.readMemory(this.condOffset); + + if (condition.toBigInt() == 0n) { + this.incrementPc(machineState); + } else { + machineState.pc = this.jumpOffset; + } + } +} + +/** -*/ +export class InternalCall extends Instruction { + static type: string = 'INTERNALCALL'; + static numberOfOperands = 1; + + constructor(private jumpOffset: number) { + super(); + } + + execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + machineState.internalCallStack.push(machineState.pc + 1); + machineState.pc = this.jumpOffset; + } +} + +/** -*/ +export class InternalReturn extends Instruction { + static type: string = 'INTERNALRETURN'; + static numberOfOperands = 0; + + constructor() { + super(); + } + + execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + const jumpOffset = machineState.internalCallStack.pop(); + if (jumpOffset === undefined) { + throw new InternalCallStackEmptyError(); + } + machineState.pc = jumpOffset; + } +} + +/** + * Thrown if the internal call stack is popped when it is empty + */ +export class InternalCallStackEmptyError extends Error { + constructor() { + super('Internal call stack is empty'); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 9a97ab21a00..c77a1a9ddd3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -9,4 +9,12 @@ export const AVM_OPCODE_BYTE_LENGTH = 1; */ export abstract class Instruction { abstract execute(machineState: AvmMachineState, stateManager: AvmStateManager): void; + + incrementPc(machineState: AvmMachineState): void { + machineState.pc++; + } + + halt(machineState: AvmMachineState): void { + machineState.halted = true; + } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts index 5d8c8921daf..1211205b979 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts @@ -6,7 +6,7 @@ import { } from './arithmetic.js'; //import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; //import { Eq, Lt, Lte } from './comparators.js'; -import { Return } from './control_flow.js'; +import { InternalCall, InternalReturn, Jump, JumpI, Return } from './control_flow.js'; import { Instruction } from './instruction.js'; import { CalldataCopy, @@ -72,10 +72,10 @@ export const INSTRUCTION_SET: Map = ne //[Opcode.L2GASLEFT, L2gasleft], //[Opcode.DAGASLEFT, Dagasleft], //// Machine State - Internal Control Flow - //[Opcode.JUMP, Jump], - //[Opcode.JUMPI, Jumpi], - //[Opcode.INTERNALCALL, Internalcall], - //[Opcode.INTERNALRETURN, Internalreturn], + [Opcode.JUMP, Jump], + [Opcode.JUMPI, JumpI], + [Opcode.INTERNALCALL, InternalCall], + [Opcode.INTERNALRETURN, InternalReturn], //// Machine State - Memory //[Opcode.SET, Set], //[Opcode.MOV, Mov], diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index bf14c1c235b..c856645eef9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -5,56 +5,72 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; /** - */ -export class Set implements Instruction { +export class Set extends Instruction { static type: string = 'SET'; static numberOfOperands = 2; - constructor(private constt: bigint, private destOffset: number) {} + constructor(private constt: bigint, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const dest = new Fr(this.constt); machineState.writeMemory(this.destOffset, dest); + + this.incrementPc(machineState); } } // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3987): tags are not implemented yet - this will behave as a mov /** - */ -export class Cast implements Instruction { +export class Cast extends Instruction { static type: string = 'CAST'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a = machineState.readMemory(this.aOffset); machineState.writeMemory(this.destOffset, a); + + this.incrementPc(machineState); } } /** - */ -export class Mov implements Instruction { +export class Mov extends Instruction { static type: string = 'MOV'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) {} + constructor(private aOffset: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const a = machineState.readMemory(this.aOffset); machineState.writeMemory(this.destOffset, a); + + this.incrementPc(machineState); } } /** - */ -export class CalldataCopy implements Instruction { +export class CalldataCopy extends Instruction { static type: string = 'CALLDATACOPY'; static numberOfOperands = 3; - constructor(private cdOffset: number, private copySize: number, private destOffset: number) {} + constructor(private cdOffset: number, private copySize: number, private destOffset: number) { + super(); + } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const calldata = machineState.calldata.slice(this.cdOffset, this.cdOffset + this.copySize); machineState.writeMemoryChunk(this.destOffset, calldata); + + this.incrementPc(machineState); } }