From 82729cb324b74560f9206107a817b9bab98f5d6a Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 29 Jan 2024 16:53:04 +0000 Subject: [PATCH 01/25] WIP serialization --- .../src/avm/opcodes/arithmetic.test.ts | 57 ++++++++++++++++- .../src/avm/opcodes/arithmetic.ts | 33 ++++++++-- .../src/avm/opcodes/serialization.ts | 62 +++++++++++++++++++ 3 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/serialization.ts diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index a9911f8d82d..c318a9065d5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,7 +1,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { Field } from '../avm_memory_types.js'; +import { Field, TypeTag } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; @@ -16,6 +16,57 @@ describe('Arithmetic Instructions', () => { }); describe('Add', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // indirect + 0x01, + // inTag + 0x03, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + + const inst = Add.deserialize(buf); + expect(inst).toEqual( + new Add( + /*indirect=*/ 0x01, + /*inTag=*/ 0x03, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Add( + /*indirect=*/ 0x01, + /*inTag=*/ 0x03, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + console.log(inst); + + const expected = Buffer.from([ + // indirect + 0x01, + // inTag + 0x03, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should add correctly over field elements', async () => { const a = new Field(1n); const b = new Field(2n); @@ -23,7 +74,7 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Add(0, 1, 2).execute(machineState, journal); + await new Add(0, TypeTag.FIELD, 0, 1, 2).execute(machineState, journal); const expected = new Field(3n); const actual = machineState.memory.get(2); @@ -37,7 +88,7 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Add(0, 1, 2).execute(machineState, journal); + await new Add(0, TypeTag.FIELD, 0, 1, 2).execute(machineState, journal); const expected = new Field(0n); const actual = machineState.memory.get(2); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 5366c8428d6..98651fe052b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,15 +1,39 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; +import { OperandPair, OperandType, deserialize, serialize } from './serialization.js'; export class Add extends Instruction { - static type: string = 'ADD'; - static numberOfOperands = 3; - - constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { + static readonly type: string = 'ADD'; + static readonly numberOfOperands = 3; + + private static readonly operands: OperandPair[] = [ + [(c: Add) => c.indirect, OperandType.UINT8], + [(c: Add) => c.inTag, OperandType.UINT8], + [(c: Add) => c.aOffset, OperandType.UINT32], + [(c: Add) => c.bOffset, OperandType.UINT32], + [(c: Add) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + private indirect: number, + private inTag: number, + private aOffset: number, + private bOffset: number, + private dstOffset: number, + ) { super(); } + public static deserialize(buf: Buffer): Add { + const args = deserialize(buf, Add.operands) as ConstructorParameters; + return new Add(...args); + } + + public serialize(): Buffer { + return serialize(Add.operands, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -59,7 +83,6 @@ export class Mul extends Instruction { } } -/** -*/ export class Div extends Instruction { static type: string = 'DIV'; static numberOfOperands = 3; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts new file mode 100644 index 00000000000..ad4a911bd36 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts @@ -0,0 +1,62 @@ +export enum OperandType { + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, + FIELD, +} + +export type OperandPair = [(c: any) => any, OperandType]; + +function readBigUintBE(buf: Buffer, totalBytes: number): bigint { + let ret: bigint = 0n; + for (let i = 0; i < totalBytes; ++i) { + ret |= BigInt(buf.readUint8()); + ret <<= 1n; + } + return ret; +} + +function writeBigUintBE(buf: Buffer, value: bigint, totalBytes: number): void { + for (let i = totalBytes - 1; i >= 0; --i) { + buf.writeUint8(Number(value & 0xFFn)); + value >>= 1n; + } +} + +const operandSpec: Map any, (value: any) => any]> = new Map([ + [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], + [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], + [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], + [OperandType.UINT64, [8, Buffer.prototype.readBigUInt64BE, Buffer.prototype.writeBigUint64BE]], + [OperandType.UINT128, [16, readBigUintBE.bind(16), writeBigUintBE.bind(16)]], // FIXME: binds first not last argument + [OperandType.FIELD, [32, readBigUintBE.bind(32), writeBigUintBE.bind(32)]], // FIXME: binds first not last argument +]); + +export function deserialize(buf: Buffer, operands: OperandPair[]): any[] { + const argValues = []; + + for (const op of operands) { + const [_opGetter, opType] = op; + const [sizeBytes, reader, _writer] = operandSpec.get(opType)!; + argValues.push(reader.call(buf)); + buf = buf.subarray(sizeBytes); + } + + return argValues; +} + +export function serialize(operands: OperandPair[], cls: any): Buffer { + const chunks: Buffer[] = [] + + for (const op of operands) { + const [opGetter, opType] = op; + const [sizeBytes, _reader, writer] = operandSpec.get(opType)!; + const buf = Buffer.alloc(sizeBytes); + writer.call(buf, opGetter(cls)); + chunks.push(buf); + } + + return Buffer.concat(chunks); +} \ No newline at end of file From d5c824be8beef6446d7208c7cc7f0446404e0e63 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Tue, 30 Jan 2024 10:33:56 +0000 Subject: [PATCH 02/25] WIP serialization --- .../src/avm/opcodes/arithmetic.test.ts | 1 - .../src/avm/opcodes/serialization.test.ts | 61 +++++++++++++++++++ .../src/avm/opcodes/serialization.ts | 23 +++---- 3 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index c318a9065d5..8db5d263279 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -50,7 +50,6 @@ describe('Arithmetic Instructions', () => { /*bOffset=*/ 0x23456789, /*dstOffset=*/ 0x3456789a, ); - console.log(inst); const expected = Buffer.from([ // indirect diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts new file mode 100644 index 00000000000..d0508e3f017 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts @@ -0,0 +1,61 @@ +import { OperandPair, OperandType, deserialize, serialize } from './serialization.js'; + +class C { + constructor(private a: number, private b: number, private c: number, private d: bigint, private e: bigint) {} + static readonly operands: OperandPair[] = [ + [(c: C) => c.a, OperandType.UINT8], + [(c: C) => c.b, OperandType.UINT16], + [(c: C) => c.c, OperandType.UINT32], + [(c: C) => c.d, OperandType.UINT64], + [(c: C) => c.e, OperandType.UINT128], + ]; +} + +describe('Serialization', () => { + it('Should serialize all types from OperandPair[]', () => { + const instance = new C(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); + const actual: Buffer = serialize(C.operands, instance); + + expect(actual).toEqual( + Buffer.from( + [ + // a + '12', + // b + '1234', + // c + '12345678', + // d + '1234567887654321', + // e + '1234567887654321ABCDEF0000FEDCBA', + ].join(''), + 'hex', + ), + ); + }); + + it('Should deserialize all types from OperandPair[]', () => { + const buffer = Buffer.from( + [ + // a + '12', + // b + '1234', + // c + '12345678', + // d + '1234567887654321', + // e + '1234567887654321ABCDEF0000FEDCBA', + ].join(''), + 'hex', + ); + + const params = deserialize(buffer, C.operands) as ConstructorParameters; + + const actual = new C(...params); + const expected = new C(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); + expect(actual).toEqual(expected); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts index ad4a911bd36..a04e6d6f59e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts @@ -9,19 +9,21 @@ export enum OperandType { export type OperandPair = [(c: any) => any, OperandType]; -function readBigUintBE(buf: Buffer, totalBytes: number): bigint { +function readBigUint128BE(this: Buffer): bigint { + const totalBytes = 16; let ret: bigint = 0n; for (let i = 0; i < totalBytes; ++i) { - ret |= BigInt(buf.readUint8()); - ret <<= 1n; + ret <<= 8n; + ret |= BigInt(this.readUint8(i)); } return ret; } -function writeBigUintBE(buf: Buffer, value: bigint, totalBytes: number): void { - for (let i = totalBytes - 1; i >= 0; --i) { - buf.writeUint8(Number(value & 0xFFn)); - value >>= 1n; +function writeBigUint128BE(this: Buffer, value: bigint): void { + const totalBytes = 16; + for (let offset = totalBytes - 1; offset >= 0; --offset) { + this.writeUint8(Number(value & 0xffn), offset); + value >>= 8n; } } @@ -30,8 +32,7 @@ const operandSpec: Map any, (value: any) => any]> = [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], [OperandType.UINT64, [8, Buffer.prototype.readBigUInt64BE, Buffer.prototype.writeBigUint64BE]], - [OperandType.UINT128, [16, readBigUintBE.bind(16), writeBigUintBE.bind(16)]], // FIXME: binds first not last argument - [OperandType.FIELD, [32, readBigUintBE.bind(32), writeBigUintBE.bind(32)]], // FIXME: binds first not last argument + [OperandType.UINT128, [16, readBigUint128BE, writeBigUint128BE]], ]); export function deserialize(buf: Buffer, operands: OperandPair[]): any[] { @@ -48,7 +49,7 @@ export function deserialize(buf: Buffer, operands: OperandPair[]): any[] { } export function serialize(operands: OperandPair[], cls: any): Buffer { - const chunks: Buffer[] = [] + const chunks: Buffer[] = []; for (const op of operands) { const [opGetter, opType] = op; @@ -59,4 +60,4 @@ export function serialize(operands: OperandPair[], cls: any): Buffer { } return Buffer.concat(chunks); -} \ No newline at end of file +} From 43468e5dfebd3b05d7336263353bdcd797f6e5f8 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Tue, 30 Jan 2024 12:36:52 +0000 Subject: [PATCH 03/25] WIP more serialization --- .../acir-simulator/src/avm/buffer_cursor.ts | 71 ++++++ .../src/avm/opcodes/arithmetic.ts | 44 +++- .../src/avm/opcodes/decode_bytecode.test.ts | 36 --- .../src/avm/opcodes/decode_bytecode.ts | 41 --- .../avm/opcodes/encode_to_bytecode.test.ts | 30 --- .../src/avm/opcodes/encode_to_bytecode.ts | 34 --- .../src/avm/opcodes/instruction_set.ts | 98 -------- .../acir-simulator/src/avm/opcodes/opcodes.ts | 87 ------- .../src/avm/opcodes/serialization.test.ts | 83 +++++-- .../src/avm/opcodes/serialization.ts | 234 +++++++++++++++++- 10 files changed, 398 insertions(+), 360 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/buffer_cursor.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.test.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.test.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts diff --git a/yarn-project/acir-simulator/src/avm/buffer_cursor.ts b/yarn-project/acir-simulator/src/avm/buffer_cursor.ts new file mode 100644 index 00000000000..7bc0e7e13bb --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/buffer_cursor.ts @@ -0,0 +1,71 @@ +import { strict as assert } from 'assert'; + +/* + * A Buffer-like class that automatically advances the position. +*/ +export class BufferCursor { + constructor(private _buffer: Buffer, private _position: number = 0) {} + + public get position(): number { + return this._position; + } + + public get eof(): boolean { + return this._position === this._buffer.length; + } + + public get buffer(): Buffer { + return this._buffer; + } + + public bufferAtPosition(): Buffer { + return this._buffer.subarray(this._position); + } + + public advance(n: number): void { + this._position += n; + assert(n < this._buffer.length); + } + + public readUint8(): number { + const ret = this._buffer.readUint8(this._position); + this._position += 1; + return ret; + } + + public readUint16LE(): number { + const ret = this._buffer.readUint16LE(this._position); + this._position += 2; + return ret; + } + + public readUint16BE(): number { + const ret = this._buffer.readUint16BE(this._position); + this._position += 2; + return ret; + } + + public readUint32LE(): number { + const ret = this._buffer.readUint32LE(this._position); + this._position += 4; + return ret; + } + + public readUint32BE(): number { + const ret = this._buffer.readUint32BE(this._position); + this._position += 4; + return ret; + } + + public readBigInt64LE(): bigint { + const ret = this._buffer.readBigInt64LE(this._position); + this._position += 8; + return ret; + } + + public readBigInt64BE(): bigint { + const ret = this._buffer.readBigInt64BE(this._position); + this._position += 8; + return ret; + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 98651fe052b..d7b1d979475 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,13 +1,15 @@ import { AvmMachineState } from '../avm_machine_state.js'; +import { BufferCursor } from '../buffer_cursor.js'; import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -import { OperandPair, OperandType, deserialize, serialize } from './serialization.js'; +import { Opcode, OperandPair, OperandType, deserialize, serialize } from './serialization.js'; export class Add extends Instruction { static readonly type: string = 'ADD'; - static readonly numberOfOperands = 3; + static readonly opcode = Opcode.ADD; - private static readonly operands: OperandPair[] = [ + // Instruction wire format without opcode. + private static readonly wireFormat: OperandPair[] = [ [(c: Add) => c.indirect, OperandType.UINT8], [(c: Add) => c.inTag, OperandType.UINT8], [(c: Add) => c.aOffset, OperandType.UINT32], @@ -25,13 +27,13 @@ export class Add extends Instruction { super(); } - public static deserialize(buf: Buffer): Add { - const args = deserialize(buf, Add.operands) as ConstructorParameters; + public static deserialize(buf: BufferCursor): Add { + const args = deserialize(buf, Add.wireFormat) as ConstructorParameters; return new Add(...args); } public serialize(): Buffer { - return serialize(Add.operands, this); + return serialize(Add.wireFormat, this); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -46,10 +48,25 @@ export class Add extends Instruction { } export class Sub extends Instruction { - static type: string = 'SUB'; - static numberOfOperands = 3; + static readonly type: string = 'SUB'; + static readonly opcode = Opcode.SUB; + + // Instruction wire format without opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: Sub) => c.indirect, OperandType.UINT8], + [(c: Sub) => c.inTag, OperandType.UINT8], + [(c: Sub) => c.aOffset, OperandType.UINT32], + [(c: Sub) => c.bOffset, OperandType.UINT32], + [(c: Sub) => c.dstOffset, OperandType.UINT32], + ]; - constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { + constructor( + private indirect: number, + private inTag: number, + private aOffset: number, + private bOffset: number, + private dstOffset: number, + ) { super(); } @@ -62,6 +79,15 @@ export class Sub extends Instruction { this.incrementPc(machineState); } + + public static deserialize(buf: BufferCursor): Sub { + const args = deserialize(buf, Sub.wireFormat) as ConstructorParameters; + return new Sub(...args); + } + + public serialize(): Buffer { + return serialize(Sub.wireFormat, this); + } } export class Mul extends Instruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.test.ts deleted file mode 100644 index 57502440e2d..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Add, Sub } from './arithmetic.js'; -import { decodeBytecode } from './decode_bytecode.js'; -import { AVM_OPCODE_BYTE_LENGTH, AVM_OPERAND_BYTE_LENGTH, Instruction } from './instruction.js'; - -describe('Avm Decoder', () => { - const toByte = (num: number): Buffer => { - const buf = Buffer.alloc(AVM_OPCODE_BYTE_LENGTH); - buf.writeUInt8(num); - return buf; - }; - const to4Byte = (num: number): Buffer => { - const buf = Buffer.alloc(AVM_OPERAND_BYTE_LENGTH); - buf.writeUInt32BE(num); - return buf; - }; - - it('Should read bytecode buffer into a list of opcodes', () => { - const opcode = 1; - const opcode2 = 2; - const a = 1; - const b = 2; - const c = 3; - - const ops = toByte(opcode); - const ops2 = toByte(opcode2); - const as = to4Byte(a); - const bs = to4Byte(b); - const cs = to4Byte(c); - const bytecode = Buffer.concat([ops, as, bs, cs, ops2, as, bs, cs]); - - const expectedInstructions: Instruction[] = [new Add(a, b, c), new Sub(a, b, c)]; - - const instructions = decodeBytecode(bytecode); - expect(instructions).toEqual(expectedInstructions); - }); -}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts b/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts deleted file mode 100644 index 33533f31abb..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { AVM_OPCODE_BYTE_LENGTH, AVM_OPERAND_BYTE_LENGTH, Instruction } from './instruction.js'; -import { INSTRUCTION_SET } from './instruction_set.js'; -import { Opcode } from './opcodes.js'; - -/** - * Convert a buffer of bytecode into an array of instructions - * @param bytecode - Buffer of bytecode - * @returns Bytecode decoded into an ordered array of Instructions - */ -export function decodeBytecode(bytecode: Buffer): Instruction[] { - let bytePtr = 0; - const bytecodeLength = bytecode.length; - - const instructions: Instruction[] = []; - - while (bytePtr < bytecodeLength) { - const opcodeByte = bytecode[bytePtr]; - bytePtr += AVM_OPCODE_BYTE_LENGTH; - if (!(opcodeByte in Opcode)) { - throw new Error(`Opcode 0x${opcodeByte.toString(16)} not implemented`); - } - const opcode = opcodeByte as Opcode; - - const instructionType = INSTRUCTION_SET.get(opcode); - if (instructionType === undefined) { - throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); - } - const numberOfOperands = instructionType.numberOfOperands; - const operands: number[] = []; - for (let i = 0; i < numberOfOperands; i++) { - // TODO: support constants which might not be u32s - const operand = bytecode.readUInt32BE(bytePtr); - bytePtr += AVM_OPERAND_BYTE_LENGTH; - operands.push(operand); - } - - instructions.push(new instructionType(...operands)); - } - - return instructions; -} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.test.ts deleted file mode 100644 index 8b1ea033dee..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { encodeToBytecode } from './encode_to_bytecode.js'; -import { AVM_OPCODE_BYTE_LENGTH, AVM_OPERAND_BYTE_LENGTH } from './instruction.js'; -import { Opcode } from './opcodes.js'; - -describe('Avm Encoder', () => { - const toByte = (num: number): Buffer => { - const buf = Buffer.alloc(AVM_OPCODE_BYTE_LENGTH); - buf.writeUInt8(num); - return buf; - }; - const to4Byte = (num: number): Buffer => { - const buf = Buffer.alloc(AVM_OPERAND_BYTE_LENGTH); - buf.writeUInt32BE(num); - return buf; - }; - - it('Should properly encode instructions into bytecode buffers', () => { - const addArgs = [0, 1, 2]; - const subArgs = [3, 4, 5]; - - const addBytecode = encodeToBytecode(Opcode.ADD, addArgs); - const subBytecode = encodeToBytecode(Opcode.SUB, subArgs); - - const expectedAddBytecode = Buffer.concat([toByte(Opcode.ADD), to4Byte(0), to4Byte(1), to4Byte(2)]); - const expectedSubBytecode = Buffer.concat([toByte(Opcode.SUB), to4Byte(3), to4Byte(4), to4Byte(5)]); - - expect(addBytecode).toEqual(expectedAddBytecode); - expect(subBytecode).toEqual(expectedSubBytecode); - }); -}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts b/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts deleted file mode 100644 index 105c0f808ab..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { AVM_OPCODE_BYTE_LENGTH, AVM_OPERAND_BYTE_LENGTH } from './instruction.js'; -import { INSTRUCTION_SET } from './instruction_set.js'; -import { Opcode } from './opcodes.js'; - -/** - * Encode an instruction (opcode & arguments) to bytecode. - * @param opcode - the opcode to encode - * @param args - the arguments to encode - * @returns the bytecode for this one instruction - */ -export function encodeToBytecode(opcode: Opcode, args: number[]): Buffer { - const instructionType = INSTRUCTION_SET.get(opcode); - if (instructionType === undefined) { - throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); - } - - const numberOfOperands = instructionType.numberOfOperands; - if (args.length !== numberOfOperands) { - throw new Error( - `Opcode 0x${opcode.toString(16)} expects ${numberOfOperands} arguments, but ${args.length} were provided`, - ); - } - - const bytecode = Buffer.alloc(AVM_OPCODE_BYTE_LENGTH + numberOfOperands * AVM_OPERAND_BYTE_LENGTH); - - let bytePtr = 0; - bytecode.writeUInt8(opcode as number, bytePtr); - bytePtr += AVM_OPCODE_BYTE_LENGTH; - for (let i = 0; i < args.length; i++) { - bytecode.writeUInt32BE(args[i], bytePtr); - bytePtr += AVM_OPERAND_BYTE_LENGTH; - } - return bytecode; -} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts deleted file mode 100644 index e63fe02dc20..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Add, Div, Mul, Sub } from './arithmetic.js'; -import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; -import { InternalCall, InternalReturn, Jump, JumpI, Return } from './control_flow.js'; -// import { Call } from './external_calls.js'; -import { Instruction } from './instruction.js'; -import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; -import { Opcode } from './opcodes.js'; -//import { Eq, Lt, Lte } from './comparators.js'; -import { SLoad, SStore } from './storage.js'; - -type InstructionConstructor = new (...args: any[]) => Instruction; - -type InstructionConstructorAndMembers = InstructionConstructor & { - numberOfOperands: number; -}; - -export const INSTRUCTION_SET: Map = new Map( - new Array<[Opcode, InstructionConstructorAndMembers]>( - // Compute - // Compute - Arithmetic - [Opcode.ADD, Add], - [Opcode.SUB, Sub], - [Opcode.MUL, Mul], - [Opcode.DIV, Div], - //// Compute - Comparators - //[Opcode.EQ, Eq], - //[Opcode.LT, Lt], - //[Opcode.LTE, Lte], - //// Compute - Bitwise - [Opcode.AND, And], - [Opcode.OR, Or], - [Opcode.XOR, Xor], - [Opcode.NOT, Not], - [Opcode.SHL, Shl], - [Opcode.SHR, Shr], - //// Compute - Type Conversions - [Opcode.CAST, Cast], - - //// Execution Environment - //[Opcode.ADDRESS, Address], - //[Opcode.STORAGEADDRESS, Storageaddress], - //[Opcode.ORIGIN, Origin], - //[Opcode.SENDER, Sender], - //[Opcode.PORTAL, Portal], - //[Opcode.FEEPERL1GAS, Feeperl1gas], - //[Opcode.FEEPERL2GAS, Feeperl2gas], - //[Opcode.FEEPERDAGAS, Feeperdagas], - //[Opcode.CONTRACTCALLDEPTH, Contractcalldepth], - //// Execution Environment - Globals - //[Opcode.CHAINID, Chainid], - //[Opcode.VERSION, Version], - //[Opcode.BLOCKNUMBER, Blocknumber], - //[Opcode.TIMESTAMP, Timestamp], - //[Opcode.COINBASE, Coinbase], - //[Opcode.BLOCKL1GASLIMIT, Blockl1gaslimit], - //[Opcode.BLOCKL2GASLIMIT, Blockl2gaslimit], - //[Opcode.BLOCKDAGASLIMIT, Blockdagaslimit], - // Execution Environment - Calldata - [Opcode.CALLDATACOPY, CalldataCopy], - - //// Machine State - // Machine State - Gas - //[Opcode.L1GASLEFT, L1gasleft], - //[Opcode.L2GASLEFT, L2gasleft], - //[Opcode.DAGASLEFT, Dagasleft], - //// Machine State - Internal Control Flow - [Opcode.JUMP, Jump], - [Opcode.JUMPI, JumpI], - [Opcode.INTERNALCALL, InternalCall], - [Opcode.INTERNALRETURN, InternalReturn], - //// Machine State - Memory - [Opcode.SET, Set], - [Opcode.MOV, Mov], - [Opcode.CMOV, CMov], - - //// World State - //[Opcode.BLOCKHEADERBYNUMBER, Blockheaderbynumber], - [Opcode.SLOAD, SLoad], // Public Storage - [Opcode.SSTORE, SStore], // Public Storage - //[Opcode.READL1TOL2MSG, Readl1tol2msg], // Messages - //[Opcode.SENDL2TOL1MSG, Sendl2tol1msg], // Messages - //[Opcode.EMITNOTEHASH, Emitnotehash], // Notes & Nullifiers - //[Opcode.EMITNULLIFIER, Emitnullifier], // Notes & Nullifiers - - //// Accrued Substate - //[Opcode.EMITUNENCRYPTEDLOG, Emitunencryptedlog], - - //// Control Flow - Contract Calls - // [Opcode.CALL, Call], - //[Opcode.STATICCALL, Staticcall], - [Opcode.RETURN, Return], - //[Opcode.REVERT, Revert], - - //// Gadgets - //[Opcode.KECCAK, Keccak], - //[Opcode.POSEIDON, Poseidon], - ), -); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts b/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts deleted file mode 100644 index 97bdfa3181d..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). - * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set - */ -export enum Opcode { - // Compute - // Compute - Arithmetic - ADD = 0x00, - SUB = 0x01, - MUL = 0x02, - DIV = 0x03, - // Compute - Comparators - EQ = 0x04, - LT = 0x05, - LTE = 0x06, - // Compute - Bitwise - AND = 0x07, - OR = 0x08, - XOR = 0x09, - NOT = 0x0a, - SHL = 0x0b, - SHR = 0x0c, - // Compute - Type Conversions - CAST = 0x0d, - - // Execution Environment - ADDRESS = 0x0e, - STORAGEADDRESS = 0x0f, - ORIGIN = 0x10, - SENDER = 0x11, - PORTAL = 0x12, - FEEPERL1GAS = 0x13, - FEEPERL2GAS = 0x14, - FEEPERDAGAS = 0x15, - CONTRACTCALLDEPTH = 0x16, - // Execution Environment - Globals - CHAINID = 0x17, - VERSION = 0x18, - BLOCKNUMBER = 0x19, - TIMESTAMP = 0x1a, - COINBASE = 0x1b, - BLOCKL1GASLIMIT = 0x1c, - BLOCKL2GASLIMIT = 0x1d, - BLOCKDAGASLIMIT = 0x1e, - // Execution Environment - Calldata - CALLDATACOPY = 0x1f, - - // Machine State - // Machine State - Gas - L1GASLEFT = 0x20, - L2GASLEFT = 0x21, - DAGASLEFT = 0x22, - // Machine State - Internal Control Flow - JUMP = 0x23, - JUMPI = 0x24, - INTERNALCALL = 0x25, - INTERNALRETURN = 0x26, - // Machine State - Memory - SET = 0x27, - MOV = 0x28, - CMOV = 0x29, - - // World State - BLOCKHEADERBYNUMBER = 0x2a, - SLOAD = 0x2b, // Public Storage - SSTORE = 0x2c, // Public Storage - READL1TOL2MSG = 0x2d, // Messages - SENDL2TOL1MSG = 0x2e, // Messages - EMITNOTEHASH = 0x2f, // Notes & Nullifiers - EMITNULLIFIER = 0x30, // Notes & Nullifiers - - // Accrued Substate - EMITUNENCRYPTEDLOG = 0x31, - - // Control Flow - Contract Calls - CALL = 0x32, - STATICCALL = 0x33, - RETURN = 0x34, - REVERT = 0x35, - - // Gadgets - KECCAK = 0x36, - POSEIDON = 0x37, - - // Add new opcodes before this - TOTAL_OPCODES_NUMBER, -} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts index d0508e3f017..0ce7c88606e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts @@ -1,20 +1,60 @@ -import { OperandPair, OperandType, deserialize, serialize } from './serialization.js'; +import { BufferCursor } from '../buffer_cursor.js'; +import { + DeserializableInstruction, + InstructionSet, + Opcode, + OperandPair, + OperandType, + decodeBytecode, + deserialize, + serialize, +} from './serialization.js'; -class C { +class InstA { constructor(private a: number, private b: number, private c: number, private d: bigint, private e: bigint) {} - static readonly operands: OperandPair[] = [ - [(c: C) => c.a, OperandType.UINT8], - [(c: C) => c.b, OperandType.UINT16], - [(c: C) => c.c, OperandType.UINT32], - [(c: C) => c.d, OperandType.UINT64], - [(c: C) => c.e, OperandType.UINT128], + + static readonly opcode: number = 1; + static readonly wireFormat: OperandPair[] = [ + [(c: InstA) => c.a, OperandType.UINT8], + [(c: InstA) => c.b, OperandType.UINT16], + [(c: InstA) => c.c, OperandType.UINT32], + [(c: InstA) => c.d, OperandType.UINT64], + [(c: InstA) => c.e, OperandType.UINT128], ]; + + public static deserialize(buf: BufferCursor): InstA { + const args = deserialize(buf, InstA.wireFormat) as ConstructorParameters; + return new InstA(...args); + } + + public serialize(): Buffer { + return serialize(InstA.wireFormat, this); + } } -describe('Serialization', () => { +class InstB { + constructor(private a: number, private b: bigint) {} + + static readonly opcode: number = 2; + static readonly wireFormat: OperandPair[] = [ + [(c: InstB) => c.a, OperandType.UINT8], + [(c: InstB) => c.b, OperandType.UINT64], + ]; + + public static deserialize(buf: BufferCursor): InstB { + const args = deserialize(buf, InstB.wireFormat) as ConstructorParameters; + return new InstB(...args); + } + + public serialize(): Buffer { + return serialize(InstB.wireFormat, this); + } +} + +describe('Instruction Serialization', () => { it('Should serialize all types from OperandPair[]', () => { - const instance = new C(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); - const actual: Buffer = serialize(C.operands, instance); + const instance = new InstA(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); + const actual: Buffer = serialize(InstA.wireFormat, instance); expect(actual).toEqual( Buffer.from( @@ -52,10 +92,25 @@ describe('Serialization', () => { 'hex', ); - const params = deserialize(buffer, C.operands) as ConstructorParameters; + const params = deserialize(new BufferCursor(buffer), InstA.wireFormat) as ConstructorParameters; - const actual = new C(...params); - const expected = new C(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); + const actual = new InstA(...params); + const expected = new InstA(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); expect(actual).toEqual(expected); }); }); + +describe('Bytecode Serialization', () => { + it('Should deserialize using instruction set', () => { + const instructionSet: InstructionSet = new Map([ + [InstA.opcode, InstA.deserialize], + [InstB.opcode, InstB.deserialize], + ]); + const a = new InstA(0, 1, 2, 3n, 4n); + const b = new InstB(1, 2n); + const bytecode = Buffer.concat([Buffer.of(InstA.opcode), a.serialize(), Buffer.of(InstB.opcode), b.serialize()]); + + const actual = decodeBytecode(bytecode, instructionSet); + expect(actual).toEqual([a, b]); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts index a04e6d6f59e..b6eb20b2bf4 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts @@ -1,15 +1,18 @@ +import { BufferCursor } from "../buffer_cursor.js"; +import { Add, Sub } from "./arithmetic.js"; // FIX: Dependency cycle +import { Instruction } from "./instruction.js"; + export enum OperandType { UINT8, UINT16, UINT32, UINT64, UINT128, - FIELD, } export type OperandPair = [(c: any) => any, OperandType]; -function readBigUint128BE(this: Buffer): bigint { +function readBigInt128BE(this: Buffer): bigint { const totalBytes = 16; let ret: bigint = 0n; for (let i = 0; i < totalBytes; ++i) { @@ -19,7 +22,7 @@ function readBigUint128BE(this: Buffer): bigint { return ret; } -function writeBigUint128BE(this: Buffer, value: bigint): void { +function writeBigInt128BE(this: Buffer, value: bigint): void { const totalBytes = 16; for (let offset = totalBytes - 1; offset >= 0; --offset) { this.writeUint8(Number(value & 0xffn), offset); @@ -27,22 +30,22 @@ function writeBigUint128BE(this: Buffer, value: bigint): void { } } -const operandSpec: Map any, (value: any) => any]> = new Map([ +const OPERAND_SPEC: Map any, (value: any) => any]> = new Map([ [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], - [OperandType.UINT64, [8, Buffer.prototype.readBigUInt64BE, Buffer.prototype.writeBigUint64BE]], - [OperandType.UINT128, [16, readBigUint128BE, writeBigUint128BE]], + [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], + [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], ]); -export function deserialize(buf: Buffer, operands: OperandPair[]): any[] { +export function deserialize(cursor: BufferCursor, operands: OperandPair[]): any[] { const argValues = []; for (const op of operands) { const [_opGetter, opType] = op; - const [sizeBytes, reader, _writer] = operandSpec.get(opType)!; - argValues.push(reader.call(buf)); - buf = buf.subarray(sizeBytes); + const [sizeBytes, reader, _writer] = OPERAND_SPEC.get(opType)!; + argValues.push(reader.call(cursor.bufferAtPosition())); + cursor.advance(sizeBytes); } return argValues; @@ -53,7 +56,7 @@ export function serialize(operands: OperandPair[], cls: any): Buffer { for (const op of operands) { const [opGetter, opType] = op; - const [sizeBytes, _reader, writer] = operandSpec.get(opType)!; + const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; const buf = Buffer.alloc(sizeBytes); writer.call(buf, opGetter(cls)); chunks.push(buf); @@ -61,3 +64,212 @@ export function serialize(operands: OperandPair[], cls: any): Buffer { return Buffer.concat(chunks); } + +/** + * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). + * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set + */ +export enum Opcode { + ADD = 0x00, + SUB = 0x01, + MUL = 0x02, + DIV = 0x03, + EQ = 0x04, + LT = 0x05, + LTE = 0x06, + AND = 0x07, + OR = 0x08, + XOR = 0x09, + NOT = 0x0a, + SHL = 0x0b, + SHR = 0x0c, + CAST = 0x0d, + ADDRESS = 0x0e, + STORAGEADDRESS = 0x0f, + ORIGIN = 0x10, + SENDER = 0x11, + PORTAL = 0x12, + FEEPERL1GAS = 0x13, + FEEPERL2GAS = 0x14, + FEEPERDAGAS = 0x15, + CONTRACTCALLDEPTH = 0x16, + CHAINID = 0x17, + VERSION = 0x18, + BLOCKNUMBER = 0x19, + TIMESTAMP = 0x1a, + COINBASE = 0x1b, + BLOCKL1GASLIMIT = 0x1c, + BLOCKL2GASLIMIT = 0x1d, + BLOCKDAGASLIMIT = 0x1e, + CALLDATACOPY = 0x1f, + L1GASLEFT = 0x20, + L2GASLEFT = 0x21, + DAGASLEFT = 0x22, + JUMP = 0x23, + JUMPI = 0x24, + INTERNALCALL = 0x25, + INTERNALRETURN = 0x26, + SET = 0x27, + MOV = 0x28, + CMOV = 0x29, + BLOCKHEADERBYNUMBER = 0x2a, + SLOAD = 0x2b, // Public Storage + SSTORE = 0x2c, // Public Storage + READL1TOL2MSG = 0x2d, // Messages + SENDL2TOL1MSG = 0x2e, // Messages + EMITNOTEHASH = 0x2f, // Notes & Nullifiers + EMITNULLIFIER = 0x30, // Notes & Nullifiers + EMITUNENCRYPTEDLOG = 0x31, + CALL = 0x32, + STATICCALL = 0x33, + RETURN = 0x34, + REVERT = 0x35, + KECCAK = 0x36, + POSEIDON = 0x37, + // Add new opcodes before this + TOTAL_OPCODES_NUMBER, +} + +export interface DeserializableInstruction { + deserialize(buf: BufferCursor): Instruction; +} + +export type InstructionSet = Map; +const INSTRUCTION_SET: InstructionSet = new Map([ + // new Array<[Opcode, InstructionConstructorAndMembers]>( + // Compute + // Compute - Arithmetic + [Add.opcode, Add], + [Sub.opcode, Sub], + // [Opcode.SUB, Sub], + // [Opcode.MUL, Mul], + // [Opcode.DIV, Div], + // //// Compute - Comparators + // //[Opcode.EQ, Eq], + // //[Opcode.LT, Lt], + // //[Opcode.LTE, Lte], + // //// Compute - Bitwise + // [Opcode.AND, And], + // [Opcode.OR, Or], + // [Opcode.XOR, Xor], + // [Opcode.NOT, Not], + // [Opcode.SHL, Shl], + // [Opcode.SHR, Shr], + // //// Compute - Type Conversions + // [Opcode.CAST, Cast], + + // //// Execution Environment + // //[Opcode.ADDRESS, Address], + // //[Opcode.STORAGEADDRESS, Storageaddress], + // //[Opcode.ORIGIN, Origin], + // //[Opcode.SENDER, Sender], + // //[Opcode.PORTAL, Portal], + // //[Opcode.FEEPERL1GAS, Feeperl1gas], + // //[Opcode.FEEPERL2GAS, Feeperl2gas], + // //[Opcode.FEEPERDAGAS, Feeperdagas], + // //[Opcode.CONTRACTCALLDEPTH, Contractcalldepth], + // //// Execution Environment - Globals + // //[Opcode.CHAINID, Chainid], + // //[Opcode.VERSION, Version], + // //[Opcode.BLOCKNUMBER, Blocknumber], + // //[Opcode.TIMESTAMP, Timestamp], + // //[Opcode.COINBASE, Coinbase], + // //[Opcode.BLOCKL1GASLIMIT, Blockl1gaslimit], + // //[Opcode.BLOCKL2GASLIMIT, Blockl2gaslimit], + // //[Opcode.BLOCKDAGASLIMIT, Blockdagaslimit], + // // Execution Environment - Calldata + // [Opcode.CALLDATACOPY, CalldataCopy], + + // //// Machine State + // // Machine State - Gas + // //[Opcode.L1GASLEFT, L1gasleft], + // //[Opcode.L2GASLEFT, L2gasleft], + // //[Opcode.DAGASLEFT, Dagasleft], + // //// Machine State - Internal Control Flow + // [Opcode.JUMP, Jump], + // [Opcode.JUMPI, JumpI], + // [Opcode.INTERNALCALL, InternalCall], + // [Opcode.INTERNALRETURN, InternalReturn], + // //// Machine State - Memory + // [Opcode.SET, Set], + // [Opcode.MOV, Mov], + // [Opcode.CMOV, CMov], + + // //// World State + // //[Opcode.BLOCKHEADERBYNUMBER, Blockheaderbynumber], + // [Opcode.SLOAD, SLoad], // Public Storage + // [Opcode.SSTORE, SStore], // Public Storage + // //[Opcode.READL1TOL2MSG, Readl1tol2msg], // Messages + // //[Opcode.SENDL2TOL1MSG, Sendl2tol1msg], // Messages + // //[Opcode.EMITNOTEHASH, Emitnotehash], // Notes & Nullifiers + // //[Opcode.EMITNULLIFIER, Emitnullifier], // Notes & Nullifiers + + // //// Accrued Substate + // //[Opcode.EMITUNENCRYPTEDLOG, Emitunencryptedlog], + + // //// Control Flow - Contract Calls + // // [Opcode.CALL, Call], + // //[Opcode.STATICCALL, Staticcall], + // [Opcode.RETURN, Return], + // //[Opcode.REVERT, Revert], + + // //// Gadgets + // //[Opcode.KECCAK, Keccak], + // //[Opcode.POSEIDON, Poseidon], +]//), +); + +/** + * Encode an instruction (opcode & arguments) to bytecode. + * @param opcode - the opcode to encode + * @param args - the arguments to encode + * @returns the bytecode for this one instruction + */ +// export function encodeToBytecode(opcode: Opcode, args: number[]): Buffer { +// const instructionType = INSTRUCTION_SET.get(opcode); +// if (instructionType === undefined) { +// throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); +// } + +// const numberOfOperands = instructionType.numberOfOperands; +// if (args.length !== numberOfOperands) { +// throw new Error( +// `Opcode 0x${opcode.toString(16)} expects ${numberOfOperands} arguments, but ${args.length} were provided`, +// ); +// } + +// const bytecode = Buffer.alloc(AVM_OPCODE_BYTE_LENGTH + numberOfOperands * AVM_OPERAND_BYTE_LENGTH); + +// let bytePtr = 0; +// bytecode.writeUInt8(opcode as number, bytePtr); +// bytePtr += AVM_OPCODE_BYTE_LENGTH; +// for (let i = 0; i < args.length; i++) { +// bytecode.writeUInt32BE(args[i], bytePtr); +// bytePtr += AVM_OPERAND_BYTE_LENGTH; +// } +// return bytecode; +// } + +/** + * Convert a buffer of bytecode into an array of instructions + * @param bytecode - Buffer of bytecode + * @returns Bytecode decoded into an ordered array of Instructions + */ +export function decodeBytecode(bytecode: Buffer, instructionSet: InstructionSet = INSTRUCTION_SET): Instruction[] { + const instructions: Instruction[] = []; + const cursor = new BufferCursor(bytecode); + + while (bytecode.length > 0) { + const opcode: Opcode = cursor.readUint8(); + const instructionDeserializerOrUndef = instructionSet.get(opcode); + if (instructionDeserializerOrUndef === undefined) { + throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); + } + + const instructionDeserializer: DeserializableInstruction = instructionDeserializerOrUndef; + const i: Instruction = instructionDeserializer.deserialize(cursor); + instructions.push(i); + } + + return instructions; +} From 1a829fcba9f8474aa8ff8add69f878565d8a643b Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Tue, 30 Jan 2024 15:12:55 +0000 Subject: [PATCH 04/25] bytecode serialization --- .../acir-simulator/src/avm/buffer_cursor.ts | 6 +- .../src/avm/bytecode_serialization.test.ts | 49 ++++++ ...alization.ts => bytecode_serialization.ts} | 144 ++---------------- .../src/avm/opcodes/arithmetic.ts | 2 +- ...t.ts => instruction_serialization.test.ts} | 54 +------ .../avm/opcodes/instruction_serialization.ts | 129 ++++++++++++++++ 6 files changed, 192 insertions(+), 192 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts rename yarn-project/acir-simulator/src/avm/{opcodes/serialization.ts => bytecode_serialization.ts} (58%) rename yarn-project/acir-simulator/src/avm/opcodes/{serialization.test.ts => instruction_serialization.test.ts} (53%) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts diff --git a/yarn-project/acir-simulator/src/avm/buffer_cursor.ts b/yarn-project/acir-simulator/src/avm/buffer_cursor.ts index 7bc0e7e13bb..a938218c3e4 100644 --- a/yarn-project/acir-simulator/src/avm/buffer_cursor.ts +++ b/yarn-project/acir-simulator/src/avm/buffer_cursor.ts @@ -6,15 +6,15 @@ import { strict as assert } from 'assert'; export class BufferCursor { constructor(private _buffer: Buffer, private _position: number = 0) {} - public get position(): number { + public position(): number { return this._position; } - public get eof(): boolean { + public eof(): boolean { return this._position === this._buffer.length; } - public get buffer(): Buffer { + public buffer(): Buffer { return this._buffer; } diff --git a/yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts b/yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts new file mode 100644 index 00000000000..c5c46004c60 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts @@ -0,0 +1,49 @@ +import { BufferCursor } from './buffer_cursor.js'; +import { InstructionSet, decodeBytecode } from './bytecode_serialization.js'; +import { Opcode } from './opcodes/instruction_serialization.js'; + +class InstA { + constructor(private n: number) {} + static readonly opcode: number = 1; + + public static deserialize(buf: BufferCursor): InstA { + return new InstA(buf.readUint16BE()); + } + + public serialize(): Buffer { + const buf = Buffer.alloc(2); + buf.writeUint16BE(this.n); + return buf; + } +} + +class InstB { + constructor(private n: bigint) {} + static readonly opcode: number = 2; + + public static deserialize(buf: BufferCursor): InstB { + // just something simple! + return new InstB(buf.readBigInt64BE()); + } + + public serialize(): Buffer { + const buf = Buffer.alloc(8); + buf.writeBigInt64BE(this.n); + return buf; + } +} + +describe('Bytecode Serialization', () => { + it('Should deserialize using instruction set', () => { + const instructionSet: InstructionSet = new Map([ + [InstA.opcode, InstA], + [InstB.opcode, InstB], + ]); + const a = new InstA(1234); + const b = new InstB(5678n); + const bytecode = Buffer.concat([Buffer.of(InstA.opcode), a.serialize(), Buffer.of(InstB.opcode), b.serialize()]); + + const actual = decodeBytecode(bytecode, instructionSet); + expect(actual).toEqual([a, b]); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts b/yarn-project/acir-simulator/src/avm/bytecode_serialization.ts similarity index 58% rename from yarn-project/acir-simulator/src/avm/opcodes/serialization.ts rename to yarn-project/acir-simulator/src/avm/bytecode_serialization.ts index b6eb20b2bf4..c80b4f707d0 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/serialization.ts +++ b/yarn-project/acir-simulator/src/avm/bytecode_serialization.ts @@ -1,142 +1,16 @@ -import { BufferCursor } from "../buffer_cursor.js"; -import { Add, Sub } from "./arithmetic.js"; // FIX: Dependency cycle -import { Instruction } from "./instruction.js"; - -export enum OperandType { - UINT8, - UINT16, - UINT32, - UINT64, - UINT128, -} - -export type OperandPair = [(c: any) => any, OperandType]; - -function readBigInt128BE(this: Buffer): bigint { - const totalBytes = 16; - let ret: bigint = 0n; - for (let i = 0; i < totalBytes; ++i) { - ret <<= 8n; - ret |= BigInt(this.readUint8(i)); - } - return ret; -} - -function writeBigInt128BE(this: Buffer, value: bigint): void { - const totalBytes = 16; - for (let offset = totalBytes - 1; offset >= 0; --offset) { - this.writeUint8(Number(value & 0xffn), offset); - value >>= 8n; - } -} - -const OPERAND_SPEC: Map any, (value: any) => any]> = new Map([ - [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], - [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], - [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], - [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], - [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], -]); - -export function deserialize(cursor: BufferCursor, operands: OperandPair[]): any[] { - const argValues = []; - - for (const op of operands) { - const [_opGetter, opType] = op; - const [sizeBytes, reader, _writer] = OPERAND_SPEC.get(opType)!; - argValues.push(reader.call(cursor.bufferAtPosition())); - cursor.advance(sizeBytes); - } - - return argValues; -} - -export function serialize(operands: OperandPair[], cls: any): Buffer { - const chunks: Buffer[] = []; - - for (const op of operands) { - const [opGetter, opType] = op; - const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; - const buf = Buffer.alloc(sizeBytes); - writer.call(buf, opGetter(cls)); - chunks.push(buf); - } - - return Buffer.concat(chunks); -} - -/** - * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). - * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set - */ -export enum Opcode { - ADD = 0x00, - SUB = 0x01, - MUL = 0x02, - DIV = 0x03, - EQ = 0x04, - LT = 0x05, - LTE = 0x06, - AND = 0x07, - OR = 0x08, - XOR = 0x09, - NOT = 0x0a, - SHL = 0x0b, - SHR = 0x0c, - CAST = 0x0d, - ADDRESS = 0x0e, - STORAGEADDRESS = 0x0f, - ORIGIN = 0x10, - SENDER = 0x11, - PORTAL = 0x12, - FEEPERL1GAS = 0x13, - FEEPERL2GAS = 0x14, - FEEPERDAGAS = 0x15, - CONTRACTCALLDEPTH = 0x16, - CHAINID = 0x17, - VERSION = 0x18, - BLOCKNUMBER = 0x19, - TIMESTAMP = 0x1a, - COINBASE = 0x1b, - BLOCKL1GASLIMIT = 0x1c, - BLOCKL2GASLIMIT = 0x1d, - BLOCKDAGASLIMIT = 0x1e, - CALLDATACOPY = 0x1f, - L1GASLEFT = 0x20, - L2GASLEFT = 0x21, - DAGASLEFT = 0x22, - JUMP = 0x23, - JUMPI = 0x24, - INTERNALCALL = 0x25, - INTERNALRETURN = 0x26, - SET = 0x27, - MOV = 0x28, - CMOV = 0x29, - BLOCKHEADERBYNUMBER = 0x2a, - SLOAD = 0x2b, // Public Storage - SSTORE = 0x2c, // Public Storage - READL1TOL2MSG = 0x2d, // Messages - SENDL2TOL1MSG = 0x2e, // Messages - EMITNOTEHASH = 0x2f, // Notes & Nullifiers - EMITNULLIFIER = 0x30, // Notes & Nullifiers - EMITUNENCRYPTEDLOG = 0x31, - CALL = 0x32, - STATICCALL = 0x33, - RETURN = 0x34, - REVERT = 0x35, - KECCAK = 0x36, - POSEIDON = 0x37, - // Add new opcodes before this - TOTAL_OPCODES_NUMBER, -} +import { BufferCursor } from './buffer_cursor.js'; +import { Add, Sub } from './opcodes/index.js'; +import { Instruction } from './opcodes/instruction.js'; +import { Opcode } from './opcodes/instruction_serialization.js'; export interface DeserializableInstruction { deserialize(buf: BufferCursor): Instruction; } export type InstructionSet = Map; -const INSTRUCTION_SET: InstructionSet = new Map([ - // new Array<[Opcode, InstructionConstructorAndMembers]>( +const INSTRUCTION_SET: InstructionSet = new Map( + [ + // new Array<[Opcode, InstructionConstructorAndMembers]>( // Compute // Compute - Arithmetic [Add.opcode, Add], @@ -216,7 +90,7 @@ const INSTRUCTION_SET: InstructionSet = new Map 0) { + while (!cursor.eof()) { const opcode: Opcode = cursor.readUint8(); const instructionDeserializerOrUndef = instructionSet.get(opcode); if (instructionDeserializerOrUndef === undefined) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index d7b1d979475..e87d8074e99 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -2,7 +2,7 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { BufferCursor } from '../buffer_cursor.js'; import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -import { Opcode, OperandPair, OperandType, deserialize, serialize } from './serialization.js'; +import { Opcode, OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; export class Add extends Instruction { static readonly type: string = 'ADD'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts similarity index 53% rename from yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts rename to yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts index 0ce7c88606e..421ade8272f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts @@ -1,14 +1,5 @@ import { BufferCursor } from '../buffer_cursor.js'; -import { - DeserializableInstruction, - InstructionSet, - Opcode, - OperandPair, - OperandType, - decodeBytecode, - deserialize, - serialize, -} from './serialization.js'; +import { OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; class InstA { constructor(private a: number, private b: number, private c: number, private d: bigint, private e: bigint) {} @@ -21,34 +12,6 @@ class InstA { [(c: InstA) => c.d, OperandType.UINT64], [(c: InstA) => c.e, OperandType.UINT128], ]; - - public static deserialize(buf: BufferCursor): InstA { - const args = deserialize(buf, InstA.wireFormat) as ConstructorParameters; - return new InstA(...args); - } - - public serialize(): Buffer { - return serialize(InstA.wireFormat, this); - } -} - -class InstB { - constructor(private a: number, private b: bigint) {} - - static readonly opcode: number = 2; - static readonly wireFormat: OperandPair[] = [ - [(c: InstB) => c.a, OperandType.UINT8], - [(c: InstB) => c.b, OperandType.UINT64], - ]; - - public static deserialize(buf: BufferCursor): InstB { - const args = deserialize(buf, InstB.wireFormat) as ConstructorParameters; - return new InstB(...args); - } - - public serialize(): Buffer { - return serialize(InstB.wireFormat, this); - } } describe('Instruction Serialization', () => { @@ -99,18 +62,3 @@ describe('Instruction Serialization', () => { expect(actual).toEqual(expected); }); }); - -describe('Bytecode Serialization', () => { - it('Should deserialize using instruction set', () => { - const instructionSet: InstructionSet = new Map([ - [InstA.opcode, InstA.deserialize], - [InstB.opcode, InstB.deserialize], - ]); - const a = new InstA(0, 1, 2, 3n, 4n); - const b = new InstB(1, 2n); - const bytecode = Buffer.concat([Buffer.of(InstA.opcode), a.serialize(), Buffer.of(InstB.opcode), b.serialize()]); - - const actual = decodeBytecode(bytecode, instructionSet); - expect(actual).toEqual([a, b]); - }); -}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts new file mode 100644 index 00000000000..241dd79de23 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts @@ -0,0 +1,129 @@ +import { BufferCursor } from "../buffer_cursor.js"; + +/** + * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). + * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set + */ +export enum Opcode { + ADD = 0x00, + SUB = 0x01, + MUL = 0x02, + DIV = 0x03, + EQ = 0x04, + LT = 0x05, + LTE = 0x06, + AND = 0x07, + OR = 0x08, + XOR = 0x09, + NOT = 0x0a, + SHL = 0x0b, + SHR = 0x0c, + CAST = 0x0d, + ADDRESS = 0x0e, + STORAGEADDRESS = 0x0f, + ORIGIN = 0x10, + SENDER = 0x11, + PORTAL = 0x12, + FEEPERL1GAS = 0x13, + FEEPERL2GAS = 0x14, + FEEPERDAGAS = 0x15, + CONTRACTCALLDEPTH = 0x16, + CHAINID = 0x17, + VERSION = 0x18, + BLOCKNUMBER = 0x19, + TIMESTAMP = 0x1a, + COINBASE = 0x1b, + BLOCKL1GASLIMIT = 0x1c, + BLOCKL2GASLIMIT = 0x1d, + BLOCKDAGASLIMIT = 0x1e, + CALLDATACOPY = 0x1f, + L1GASLEFT = 0x20, + L2GASLEFT = 0x21, + DAGASLEFT = 0x22, + JUMP = 0x23, + JUMPI = 0x24, + INTERNALCALL = 0x25, + INTERNALRETURN = 0x26, + SET = 0x27, + MOV = 0x28, + CMOV = 0x29, + BLOCKHEADERBYNUMBER = 0x2a, + SLOAD = 0x2b, // Public Storage + SSTORE = 0x2c, // Public Storage + READL1TOL2MSG = 0x2d, // Messages + SENDL2TOL1MSG = 0x2e, // Messages + EMITNOTEHASH = 0x2f, // Notes & Nullifiers + EMITNULLIFIER = 0x30, // Notes & Nullifiers + EMITUNENCRYPTEDLOG = 0x31, + CALL = 0x32, + STATICCALL = 0x33, + RETURN = 0x34, + REVERT = 0x35, + KECCAK = 0x36, + POSEIDON = 0x37, + // Add new opcodes before this + TOTAL_OPCODES_NUMBER, +} + +export enum OperandType { + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, +} + +export type OperandPair = [(c: any) => any, OperandType]; + +function readBigInt128BE(this: Buffer): bigint { + const totalBytes = 16; + let ret: bigint = 0n; + for (let i = 0; i < totalBytes; ++i) { + ret <<= 8n; + ret |= BigInt(this.readUint8(i)); + } + return ret; +} + +function writeBigInt128BE(this: Buffer, value: bigint): void { + const totalBytes = 16; + for (let offset = totalBytes - 1; offset >= 0; --offset) { + this.writeUint8(Number(value & 0xffn), offset); + value >>= 8n; + } +} + +const OPERAND_SPEC: Map any, (value: any) => any]> = new Map([ + [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], + [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], + [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], + [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], + [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], +]); + +export function deserialize(cursor: BufferCursor, operands: OperandPair[]): any[] { + const argValues = []; + + for (const op of operands) { + const [_opGetter, opType] = op; + const [sizeBytes, reader, _writer] = OPERAND_SPEC.get(opType)!; + argValues.push(reader.call(cursor.bufferAtPosition())); + cursor.advance(sizeBytes); + } + + return argValues; +} + +export function serialize(operands: OperandPair[], cls: any): Buffer { + const chunks: Buffer[] = []; + + for (const op of operands) { + const [opGetter, opType] = op; + const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; + const buf = Buffer.alloc(sizeBytes); + writer.call(buf, opGetter(cls)); + chunks.push(buf); + } + + return Buffer.concat(chunks); +} \ No newline at end of file From 17c41209483aa8b8be9d15ef92276880e0608ab4 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 11:02:44 +0000 Subject: [PATCH 05/25] WIP implementing serialization for all instructions --- .../src/avm/opcodes/arithmetic.test.ts | 223 ++++++- .../src/avm/opcodes/arithmetic.ts | 112 ++-- .../src/avm/opcodes/bitwise.test.ts | 562 ++++++++++++++++-- .../acir-simulator/src/avm/opcodes/bitwise.ts | 117 +++- .../src/avm/opcodes/comparators.test.ts | 276 ++++++++- .../src/avm/opcodes/comparators.ts | 62 +- .../src/avm/opcodes/control_flow.test.ts | 39 +- .../src/avm/opcodes/instruction.ts | 9 +- .../src/avm/opcodes/instruction_impl.ts | 73 +++ .../avm/{ => serialization}/buffer_cursor.ts | 42 ++ .../bytecode_serialization.test.ts | 32 +- .../bytecode_serialization.ts | 40 +- .../instruction_serialization.test.ts | 2 +- .../instruction_serialization.ts | 11 +- 14 files changed, 1324 insertions(+), 276 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts rename yarn-project/acir-simulator/src/avm/{ => serialization}/buffer_cursor.ts (61%) rename yarn-project/acir-simulator/src/avm/{ => serialization}/bytecode_serialization.test.ts (52%) rename yarn-project/acir-simulator/src/avm/{ => serialization}/bytecode_serialization.ts (76%) rename yarn-project/acir-simulator/src/avm/{opcodes => serialization}/instruction_serialization.test.ts (97%) rename yarn-project/acir-simulator/src/avm/{opcodes => serialization}/instruction_serialization.ts (90%) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 8db5d263279..1f8164cefec 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -18,10 +18,12 @@ describe('Arithmetic Instructions', () => { describe('Add', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ + // opcode + Add.opcode, // indirect 0x01, // inTag - 0x03, + TypeTag.FIELD, // aOffset 0x12, 0x34, 0x56, 0x78, // bOffset @@ -34,7 +36,7 @@ describe('Arithmetic Instructions', () => { expect(inst).toEqual( new Add( /*indirect=*/ 0x01, - /*inTag=*/ 0x03, + /*inTag=*/ TypeTag.FIELD, /*aOffset=*/ 0x12345678, /*bOffset=*/ 0x23456789, /*dstOffset=*/ 0x3456789a, @@ -45,17 +47,19 @@ describe('Arithmetic Instructions', () => { it('Should serialize correctly', () => { const inst = new Add( /*indirect=*/ 0x01, - /*inTag=*/ 0x03, + /*inTag=*/ TypeTag.FIELD, /*aOffset=*/ 0x12345678, /*bOffset=*/ 0x23456789, /*dstOffset=*/ 0x3456789a, ); const expected = Buffer.from([ + // opcode + Add.opcode, // indirect 0x01, // inTag - 0x03, + TypeTag.FIELD, // aOffset 0x12, 0x34, 0x56, 0x78, // bOffset @@ -73,7 +77,13 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Add(0, TypeTag.FIELD, 0, 1, 2).execute(machineState, journal); + await new Add( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Field(3n); const actual = machineState.memory.get(2); @@ -87,7 +97,13 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Add(0, TypeTag.FIELD, 0, 1, 2).execute(machineState, journal); + await new Add( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Field(0n); const actual = machineState.memory.get(2); @@ -96,6 +112,60 @@ describe('Arithmetic Instructions', () => { }); describe('Sub', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Sub.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + + const inst = Sub.deserialize(buf); + expect(inst).toEqual( + new Sub( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Sub( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Sub.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should subtract correctly over field elements', async () => { const a = new Field(1n); const b = new Field(2n); @@ -103,7 +173,13 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Sub(0, 1, 2).execute(machineState, journal); + await new Sub( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Field(Field.MODULUS - 1n); const actual = machineState.memory.get(2); @@ -112,6 +188,60 @@ describe('Arithmetic Instructions', () => { }); describe('Mul', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Mul.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + + const inst = Mul.deserialize(buf); + expect(inst).toEqual( + new Mul( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Mul( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Mul.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should multiply correctly over field elements', async () => { const a = new Field(2n); const b = new Field(3n); @@ -119,7 +249,13 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Mul(0, 1, 2).execute(machineState, journal); + await new Mul( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Field(6n); const actual = machineState.memory.get(2); @@ -133,7 +269,13 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Mul(0, 1, 2).execute(machineState, journal); + await new Mul( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Field(Field.MODULUS - 3n); const actual = machineState.memory.get(2); @@ -142,6 +284,60 @@ describe('Arithmetic Instructions', () => { }); describe('Div', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Div.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + + const inst = Div.deserialize(buf); + expect(inst).toEqual( + new Add( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Div( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Div.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // aOffset + 0x12, 0x34, 0x56, 0x78, + // bOffset + 0x23, 0x45, 0x67, 0x89, + // dstOffset + 0x34, 0x56, 0x78, 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should perform field division', async () => { const a = new Field(2n); const b = new Field(3n); @@ -149,9 +345,14 @@ describe('Arithmetic Instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Div(0, 1, 2).execute(machineState, journal); + await new Div( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); - // Note const actual = machineState.memory.get(2); const recovered = actual.mul(b); expect(recovered).toEqual(a); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index e87d8074e99..de0c353ca15 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,39 +1,24 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { BufferCursor } from '../buffer_cursor.js'; import { AvmJournal } from '../journal/index.js'; -import { Instruction } from './instruction.js'; -import { Opcode, OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; +import { ThreeOperandInstruction } from './instruction_impl.js'; -export class Add extends Instruction { +export class Add extends ThreeOperandInstruction { static readonly type: string = 'ADD'; static readonly opcode = Opcode.ADD; - // Instruction wire format without opcode. - private static readonly wireFormat: OperandPair[] = [ - [(c: Add) => c.indirect, OperandType.UINT8], - [(c: Add) => c.inTag, OperandType.UINT8], - [(c: Add) => c.aOffset, OperandType.UINT32], - [(c: Add) => c.bOffset, OperandType.UINT32], - [(c: Add) => c.dstOffset, OperandType.UINT32], - ]; - - constructor( - private indirect: number, - private inTag: number, - private aOffset: number, - private bOffset: number, - private dstOffset: number, - ) { - super(); - } - - public static deserialize(buf: BufferCursor): Add { - const args = deserialize(buf, Add.wireFormat) as ConstructorParameters; - return new Add(...args); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); } - public serialize(): Buffer { - return serialize(Add.wireFormat, this); + protected get opcode() { + return Add.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Add { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Add(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -47,27 +32,21 @@ export class Add extends Instruction { } } -export class Sub extends Instruction { +export class Sub extends ThreeOperandInstruction { static readonly type: string = 'SUB'; static readonly opcode = Opcode.SUB; - // Instruction wire format without opcode. - private static readonly wireFormat: OperandPair[] = [ - [(c: Sub) => c.indirect, OperandType.UINT8], - [(c: Sub) => c.inTag, OperandType.UINT8], - [(c: Sub) => c.aOffset, OperandType.UINT32], - [(c: Sub) => c.bOffset, OperandType.UINT32], - [(c: Sub) => c.dstOffset, OperandType.UINT32], - ]; - - constructor( - private indirect: number, - private inTag: number, - private aOffset: number, - private bOffset: number, - private dstOffset: number, - ) { - super(); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Sub.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Sub { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Sub(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -79,23 +58,23 @@ export class Sub extends Instruction { this.incrementPc(machineState); } +} - public static deserialize(buf: BufferCursor): Sub { - const args = deserialize(buf, Sub.wireFormat) as ConstructorParameters; - return new Sub(...args); - } +export class Mul extends ThreeOperandInstruction { + static type: string = 'MUL'; + static readonly opcode = Opcode.MUL; - public serialize(): Buffer { - return serialize(Sub.wireFormat, this); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); } -} -export class Mul extends Instruction { - static type: string = 'MUL'; - static numberOfOperands = 3; + protected get opcode() { + return Mul.opcode; + } - constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Mul { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Mul(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -109,12 +88,21 @@ export class Mul extends Instruction { } } -export class Div extends Instruction { +export class Div extends ThreeOperandInstruction { static type: string = 'DIV'; - static numberOfOperands = 3; + static readonly opcode = Opcode.DIV; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Div.opcode; + } - constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Div { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Div(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index ae8ce802724..06dbcf104d5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -15,45 +15,357 @@ describe('Bitwise instructions', () => { journal = mock(); }); - it('Should AND correctly over integral types', async () => { - machineState.memory.set(0, new Uint32(0b11111110010011100100n)); - machineState.memory.set(1, new Uint32(0b11100100111001001111n)); + describe('AND', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + And.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: And = And.deserialize(buf); + expect(inst).toEqual( + new And( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new And( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + And.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); - await new And(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + it('Should AND correctly over integral types', async () => { + machineState.memory.set(0, new Uint32(0b11111110010011100100n)); + machineState.memory.set(1, new Uint32(0b11100100111001001111n)); - const actual = machineState.memory.get(2); - expect(actual).toEqual(new Uint32(0b11100100010001000100n)); + await new And( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); + + const actual = machineState.memory.get(2); + expect(actual).toEqual(new Uint32(0b11100100010001000100n)); + }); }); - it('Should OR correctly over integral types', async () => { - const a = new Uint32(0b11111110010011100100n); - const b = new Uint32(0b11100100111001001111n); + describe('OR', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Or.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Or = Or.deserialize(buf); + expect(inst).toEqual( + new Or( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + it('Should serialize correctly', () => { + const inst = new Or( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Or.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); - await new Or(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + it('Should OR correctly over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - const expected = new Uint32(0b11111110111011101111n); - const actual = machineState.memory.get(2); - expect(actual).toEqual(expected); + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new Or( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); + + const expected = new Uint32(0b11111110111011101111n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); }); - it('Should XOR correctly over integral types', async () => { - const a = new Uint32(0b11111110010011100100n); - const b = new Uint32(0b11100100111001001111n); + describe('XOR', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Xor.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Xor = Xor.deserialize(buf); + expect(inst).toEqual( + new Xor( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); - machineState.memory.set(0, a); - machineState.memory.set(1, b); + it('Should serialize correctly', () => { + const inst = new Xor( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Xor.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); - await new Xor(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + it('Should XOR correctly over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - const expected = new Uint32(0b00011010101010101011n); - const actual = machineState.memory.get(2); - expect(actual).toEqual(expected); + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new Xor( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); + + const expected = new Uint32(0b00011010101010101011n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); }); describe('SHR', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Shr.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Shr = Shr.deserialize(buf); + expect(inst).toEqual( + new Shr( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Shr( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Shr.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should shift correctly 0 positions over integral types', async () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0n); @@ -61,7 +373,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + await new Shr( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = a; const actual = machineState.memory.get(2); @@ -75,7 +393,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + await new Shr( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Uint32(0b00111111100100111001n); const actual = machineState.memory.get(2); @@ -89,7 +413,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + await new Shr( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Uint32(0b01n); const actual = machineState.memory.get(2); @@ -98,6 +428,78 @@ describe('Bitwise instructions', () => { }); describe('SHL', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Shl.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Shl = Shl.deserialize(buf); + expect(inst).toEqual( + new Shl( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Shl( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Shl.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should shift correctly 0 positions over integral types', async () => { const a = new Uint32(0b11111110010011100100n); const b = new Uint32(0n); @@ -105,7 +507,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shl(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + await new Shl( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = a; const actual = machineState.memory.get(2); @@ -119,7 +527,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shl(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); + await new Shl( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT32, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Uint32(0b1111111001001110010000n); const actual = machineState.memory.get(2); @@ -133,7 +547,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shl(TypeTag.UINT16, 0, 1, 2).execute(machineState, journal); + await new Shl( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT16, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Uint16(0n); const actual = machineState.memory.get(2); @@ -147,7 +567,13 @@ describe('Bitwise instructions', () => { machineState.memory.set(0, a); machineState.memory.set(1, b); - await new Shl(TypeTag.UINT16, 0, 1, 2).execute(machineState, journal); + await new Shl( + /*indirect=*/ 0, + /*inTag=*/ TypeTag.UINT16, + /*aOffset=*/ 0, + /*bOffset=*/ 1, + /*dstOffset=*/ 2, + ).execute(machineState, journal); const expected = new Uint16(0b1001001110011100n); const actual = machineState.memory.get(2); @@ -155,15 +581,75 @@ describe('Bitwise instructions', () => { }); }); - it('Should NOT correctly over integral types', async () => { - const a = new Uint16(0b0110010011100100n); + describe('NOT', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Not.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Not = Not.deserialize(buf); + expect(inst).toEqual( + new Not(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*aOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Not( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Not.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should NOT correctly over integral types', async () => { + const a = new Uint16(0b0110010011100100n); - machineState.memory.set(0, a); + machineState.memory.set(0, a); - await new Not(TypeTag.UINT16, 0, 1).execute(machineState, journal); + await new Not(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute( + machineState, + journal, + ); - const expected = new Uint16(0b1001101100011011n); // high bits! - const actual = machineState.memory.get(1); - expect(actual).toEqual(expected); + const expected = new Uint16(0b1001101100011011n); // high bits! + const actual = machineState.memory.get(1); + expect(actual).toEqual(expected); + }); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index e439a8bd447..125daa46636 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,14 +1,26 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { IntegralValue, TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js'; -export class And extends Instruction { - static type: string = 'AND'; - static numberOfOperands = 3; +export class And extends ThreeOperandInstruction { + static readonly type: string = 'AND'; + static readonly opcode = Opcode.AND; - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return And.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): And { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new And(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -24,12 +36,21 @@ export class And extends Instruction { } } -export class Or extends Instruction { - static type: string = 'OR'; - static numberOfOperands = 3; +export class Or extends ThreeOperandInstruction { + static readonly type: string = 'OR'; + static readonly opcode = Opcode.OR; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Or.opcode; + } - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Or { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Or(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -45,12 +66,21 @@ export class Or extends Instruction { } } -export class Xor extends Instruction { - static type: string = 'XOR'; - static numberOfOperands = 3; +export class Xor extends ThreeOperandInstruction { + static readonly type: string = 'XOR'; + static readonly opcode = Opcode.XOR; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Xor.opcode; + } - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Xor { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Xor(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -66,12 +96,21 @@ export class Xor extends Instruction { } } -export class Not extends Instruction { - static type: string = 'NOT'; - static numberOfOperands = 2; +export class Not extends TwoOperandInstruction { + static readonly type: string = 'NOT'; + static readonly opcode = Opcode.NOT; - constructor(private inTag: TypeTag, private aOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, inTag: number, aOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, dstOffset); + } + + protected get opcode() { + return Not.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Not { + const args = TwoOperandInstruction.deserializeBase(buf); + return new Not(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -86,12 +125,21 @@ export class Not extends Instruction { } } -export class Shl extends Instruction { - static type: string = 'SHL'; - static numberOfOperands = 3; +export class Shl extends ThreeOperandInstruction { + static readonly type: string = 'SHL'; + static readonly opcode = Opcode.SHL; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Shl.opcode; + } - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Shl { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Shl(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -107,12 +155,21 @@ export class Shl extends Instruction { } } -export class Shr extends Instruction { - static type: string = 'SHR'; - static numberOfOperands = 3; +export class Shr extends ThreeOperandInstruction { + static readonly type: string = 'SHR'; + static readonly opcode = Opcode.SHR; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Shr.opcode; + } - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Shr { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Shr(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index ff604dbf2cb..bda41c65e60 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -17,13 +17,85 @@ describe('Comparators', () => { }); describe('Eq', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Eq.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Eq = Eq.deserialize(buf); + expect(inst).toEqual( + new Eq( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Eq( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Eq.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Works on integral types', async () => { machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); [ - new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), - new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -34,9 +106,9 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); [ - new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), - new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -47,10 +119,10 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ - new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Eq(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Eq(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), ]; for (const o of ops) { @@ -60,13 +132,85 @@ describe('Comparators', () => { }); describe('Lt', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Lt.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Lt = Lt.deserialize(buf); + expect(inst).toEqual( + new Lt( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Lt( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Lt.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Works on integral types', async () => { machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); [ - new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -77,9 +221,9 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); [ - new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -90,10 +234,10 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ - new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lt(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lt(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), ]; for (const o of ops) { @@ -103,13 +247,85 @@ describe('Comparators', () => { }); describe('Lte', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Lte.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst: Lte = Lte.deserialize(buf); + expect(inst).toEqual( + new Lte( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Lte( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Lte.opcode, + // indirect + 0x01, + // inTag + TypeTag.UINT64, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Works on integral types', async () => { machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); [ - new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -120,9 +336,9 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); [ - new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); @@ -133,10 +349,10 @@ describe('Comparators', () => { machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ - new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lte(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lte(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), ]; for (const o of ops) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index f89d450dbb9..fd472256fcf 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,14 +1,25 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +import { ThreeOperandInstruction } from './instruction_impl.js'; -export class Eq extends Instruction { - static type: string = 'EQ'; - static numberOfOperands = 3; +export class Eq extends ThreeOperandInstruction { + static readonly type: string = 'EQ'; + static readonly opcode = Opcode.EQ; - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Eq.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Eq { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Eq(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { @@ -25,14 +36,24 @@ export class Eq extends Instruction { } } -export class Lt extends Instruction { - static type: string = 'Lt'; - static numberOfOperands = 3; +export class Lt extends ThreeOperandInstruction { + static readonly type: string = 'LT'; + static readonly opcode = Opcode.LT; - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); } + protected get opcode() { + return Lt.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Lt { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Lt(...args); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -47,12 +68,21 @@ export class Lt extends Instruction { } } -export class Lte extends Instruction { - static type: string = 'LTE'; - static numberOfOperands = 3; +export class Lte extends ThreeOperandInstruction { + static readonly type: string = 'LTE'; + static readonly opcode = Opcode.LTE; + + constructor(indirect: number, inTag: number, aOffset: number, bOffset: number, dstOffset: number) { + super(indirect, inTag, aOffset, bOffset, dstOffset); + } + + protected get opcode() { + return Lte.opcode; + } - constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { - super(); + public static deserialize(buf: BufferCursor | Buffer): Lte { + const args = ThreeOperandInstruction.deserializeBase(buf); + return new Lte(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { 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 index 7c04e08f246..1c5ac67d6eb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -3,15 +3,11 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { Field, TypeTag, Uint16 } from '../avm_memory_types.js'; +import { Field, Uint16 } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.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, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; import { InstructionExecutionError } from './instruction.js'; -import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Control Flow Opcodes', () => { let journal: MockProxy; @@ -118,39 +114,6 @@ describe('Control Flow Opcodes', () => { const returnInstruction = () => new InternalReturn().execute(machineState, journal); await expect(returnInstruction()).rejects.toThrow(InstructionExecutionError); }); - - it('Should increment PC on All other Instructions', async () => { - const instructions = [ - new Add(0, 1, 2), - new Sub(0, 1, 2), - new Mul(0, 1, 2), - new Lt(TypeTag.UINT16, 0, 1, 2), - new Lte(TypeTag.UINT16, 0, 1, 2), - new Eq(TypeTag.UINT16, 0, 1, 2), - new Xor(TypeTag.UINT16, 0, 1, 2), - new And(TypeTag.UINT16, 0, 1, 2), - new Or(TypeTag.UINT16, 0, 1, 2), - new Shl(TypeTag.UINT16, 0, 1, 2), - new Shr(TypeTag.UINT16, 0, 1, 2), - new Not(TypeTag.UINT16, 0, 2), - new CalldataCopy(0, 1, 2), - new Set(TypeTag.UINT16, 0n, 1), - new Mov(0, 1), - new CMov(0, 1, 2, 3), - new Cast(TypeTag.UINT16, 0, 1), - ]; - - for (const instruction of instructions) { - // Use a fresh machine state each run - const innerMachineState = new AvmMachineState(initExecutionEnvironment()); - innerMachineState.memory.set(0, new Uint16(4n)); - innerMachineState.memory.set(1, new Uint16(8n)); - innerMachineState.memory.set(2, new Uint16(12n)); - expect(innerMachineState.pc).toBe(0); - - await instruction.execute(innerMachineState, journal); - } - }); }); describe('Halting Opcodes', () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 8eb73212fd2..0d66fd51bb1 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -2,14 +2,9 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; -export const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with cpp code -export const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with cpp code - -/** - * Opcode base class - */ export abstract class Instruction { - abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; + public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; + public abstract serialize(): Buffer; incrementPc(machineState: AvmMachineState): void { machineState.pc++; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts new file mode 100644 index 00000000000..16b8785d0da --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -0,0 +1,73 @@ +import { BufferCursor } from "../serialization/buffer_cursor.js"; +import { Opcode, OperandPair, OperandType, deserialize, serialize } from "../serialization/instruction_serialization.js"; +import { Instruction } from "./instruction.js"; + +export abstract class TwoOperandInstruction extends Instruction { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: TwoOperandInstruction) => c.opcode, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.indirect, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.inTag, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.aOffset, OperandType.UINT32], + [(c: TwoOperandInstruction) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + protected indirect: number, + protected inTag: number, + protected aOffset: number, + protected dstOffset: number, + ) { + super(); + } + + protected static deserializeBase( + buf: BufferCursor | Buffer, + ): ConstructorParameters { + const res = deserialize(buf, TwoOperandInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; + } + + public serialize(): Buffer { + return serialize(TwoOperandInstruction.wireFormat, this); + } + + protected abstract get opcode(): Opcode; + } + +export abstract class ThreeOperandInstruction extends Instruction { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: ThreeOperandInstruction) => c.opcode, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.indirect, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.inTag, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.aOffset, OperandType.UINT32], + [(c: ThreeOperandInstruction) => c.bOffset, OperandType.UINT32], + [(c: ThreeOperandInstruction) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + protected indirect: number, + protected inTag: number, + protected aOffset: number, + protected bOffset: number, + protected dstOffset: number, + ) { + super(); + } + + protected static deserializeBase( + buf: BufferCursor | Buffer, + ): ConstructorParameters { + const res = deserialize(buf, ThreeOperandInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; + } + + public serialize(): Buffer { + return serialize(ThreeOperandInstruction.wireFormat, this); + } + + protected abstract get opcode(): Opcode; + } \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/buffer_cursor.ts b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts similarity index 61% rename from yarn-project/acir-simulator/src/avm/buffer_cursor.ts rename to yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts index a938218c3e4..6229d2a1a8f 100644 --- a/yarn-project/acir-simulator/src/avm/buffer_cursor.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts @@ -68,4 +68,46 @@ export class BufferCursor { this._position += 8; return ret; } + + public writeUint8(v: number) { + const ret = this._buffer.writeUint8(v, this._position); + this._position += 1; + return ret; + } + + public writeUint16LE(v: number) { + const ret = this._buffer.writeUint16LE(v, this._position); + this._position += 2; + return ret; + } + + public writeUint16BE(v: number) { + const ret = this._buffer.writeUint16BE(v, this._position); + this._position += 2; + return ret; + } + + public writeUint32LE(v: number) { + const ret = this._buffer.writeUint32LE(v, this._position); + this._position += 4; + return ret; + } + + public writeUint32BE(v: number) { + const ret = this._buffer.writeUint32BE(v, this._position); + this._position += 4; + return ret; + } + + public writeBigInt64LE(v: bigint) { + const ret = this._buffer.writeBigInt64LE(v, this._position); + this._position += 8; + return ret; + } + + public writeBigInt64BE(v: bigint) { + const ret = this._buffer.writeBigInt64BE(v, this._position); + this._position += 8; + return ret; + } } diff --git a/yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts similarity index 52% rename from yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts rename to yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts index c5c46004c60..469816b3753 100644 --- a/yarn-project/acir-simulator/src/avm/bytecode_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts @@ -1,18 +1,19 @@ import { BufferCursor } from './buffer_cursor.js'; -import { InstructionSet, decodeBytecode } from './bytecode_serialization.js'; -import { Opcode } from './opcodes/instruction_serialization.js'; +import { InstructionSet, decodeFromBytecode, encodeToBytecode } from './bytecode_serialization.js'; +import { Opcode } from './instruction_serialization.js'; class InstA { constructor(private n: number) {} static readonly opcode: number = 1; - + public static deserialize(buf: BufferCursor): InstA { return new InstA(buf.readUint16BE()); } public serialize(): Buffer { - const buf = Buffer.alloc(2); - buf.writeUint16BE(this.n); + const buf = Buffer.alloc(1 + 2); + buf.writeUint8(InstA.opcode); + buf.writeUint16BE(this.n, 1); return buf; } } @@ -22,13 +23,13 @@ class InstB { static readonly opcode: number = 2; public static deserialize(buf: BufferCursor): InstB { - // just something simple! return new InstB(buf.readBigInt64BE()); } public serialize(): Buffer { - const buf = Buffer.alloc(8); - buf.writeBigInt64BE(this.n); + const buf = Buffer.alloc(1 + 8); + buf.writeUint8(InstB.opcode); + buf.writeBigInt64BE(this.n, 1); return buf; } } @@ -41,9 +42,20 @@ describe('Bytecode Serialization', () => { ]); const a = new InstA(1234); const b = new InstB(5678n); - const bytecode = Buffer.concat([Buffer.of(InstA.opcode), a.serialize(), Buffer.of(InstB.opcode), b.serialize()]); + const bytecode = Buffer.concat([a.serialize(), b.serialize()]); + + const actual = decodeFromBytecode(bytecode, instructionSet); - const actual = decodeBytecode(bytecode, instructionSet); expect(actual).toEqual([a, b]); }); + + it('Should serialize using instruction.serialize()', () => { + const a = new InstA(1234); + const b = new InstB(5678n); + + const actual = encodeToBytecode([a, b]); + + const expected = Buffer.concat([a.serialize(), b.serialize()]); + expect(actual).toEqual(expected); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts similarity index 76% rename from yarn-project/acir-simulator/src/avm/bytecode_serialization.ts rename to yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index c80b4f707d0..aa56e785de4 100644 --- a/yarn-project/acir-simulator/src/avm/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -1,10 +1,12 @@ import { BufferCursor } from './buffer_cursor.js'; -import { Add, Sub } from './opcodes/index.js'; -import { Instruction } from './opcodes/instruction.js'; -import { Opcode } from './opcodes/instruction_serialization.js'; +import { Add, Sub } from '../opcodes/index.js'; +import { Instruction } from '../opcodes/instruction.js'; +import { Opcode } from './instruction_serialization.js'; export interface DeserializableInstruction { deserialize(buf: BufferCursor): Instruction; + opcode: Opcode; + // serialize(this: any): Buffer; } export type InstructionSet = Map; @@ -94,42 +96,22 @@ const INSTRUCTION_SET: InstructionSet = new Map i.serialize())); +} /** * Convert a buffer of bytecode into an array of instructions * @param bytecode - Buffer of bytecode * @returns Bytecode decoded into an ordered array of Instructions */ -export function decodeBytecode(bytecode: Buffer, instructionSet: InstructionSet = INSTRUCTION_SET): Instruction[] { +export function decodeFromBytecode(bytecode: Buffer, instructionSet: InstructionSet = INSTRUCTION_SET): Instruction[] { const instructions: Instruction[] = []; const cursor = new BufferCursor(bytecode); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts similarity index 97% rename from yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts rename to yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts index 421ade8272f..0e0d2428e72 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts @@ -1,4 +1,4 @@ -import { BufferCursor } from '../buffer_cursor.js'; +import { BufferCursor } from './buffer_cursor.js'; import { OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; class InstA { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts similarity index 90% rename from yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts rename to yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts index 241dd79de23..3368dec18f6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts @@ -1,4 +1,4 @@ -import { BufferCursor } from "../buffer_cursor.js"; +import { BufferCursor } from './buffer_cursor.js'; /** * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). @@ -93,7 +93,7 @@ function writeBigInt128BE(this: Buffer, value: bigint): void { } } -const OPERAND_SPEC: Map any, (value: any) => any]> = new Map([ +const OPERAND_SPEC = new Map any, (value: any) => any]>([ [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], @@ -101,8 +101,11 @@ const OPERAND_SPEC: Map any, (value: any) => any]> = [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], ]); -export function deserialize(cursor: BufferCursor, operands: OperandPair[]): any[] { +export function deserialize(cursor: BufferCursor | Buffer, operands: OperandPair[]): any[] { const argValues = []; + if (cursor instanceof Buffer) { + cursor = new BufferCursor(cursor); + } for (const op of operands) { const [_opGetter, opType] = op; @@ -126,4 +129,4 @@ export function serialize(operands: OperandPair[], cls: any): Buffer { } return Buffer.concat(chunks); -} \ No newline at end of file +} From 553470e1e9f65f7b15178816a4e9ab3f8f941852 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 11:46:33 +0000 Subject: [PATCH 06/25] memory ops serialization --- .../src/avm/opcodes/memory.test.ts | 421 ++++++++++++++++-- .../acir-simulator/src/avm/opcodes/memory.ts | 168 +++++-- 2 files changed, 517 insertions(+), 72 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 1a10c5be15b..6f7b5744645 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -18,8 +18,95 @@ describe('Memory instructions', () => { }); describe('SET', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Set.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // const (will be 128 bit) + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst = Set.deserialize(buf); + expect(inst).toEqual( + new Set( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*value=*/ 0x12345678123456781234567812345678n, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Set( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.FIELD, + /*value=*/ 0x12345678123456781234567812345678n, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Set.opcode, + // indirect + 0x01, + // inTag + TypeTag.FIELD, + // const (will be 128 bit) + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('should correctly set value and tag (uninitialized)', async () => { - await new Set(TypeTag.UINT16, /*value=*/ 1234n, /*offset=*/ 1).execute(machineState, journal); + await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT16, /*value=*/ 1234n, /*offset=*/ 1).execute( + machineState, + journal, + ); const actual = machineState.memory.get(1); const tag = machineState.memory.getTag(1); @@ -31,7 +118,10 @@ describe('Memory instructions', () => { it('should correctly set value and tag (overwriting)', async () => { machineState.memory.set(1, new Field(27)); - await new Set(TypeTag.UINT32, /*value=*/ 1234n, /*offset=*/ 1).execute(machineState, journal); + await new Set(/*indirect=*/ 0, /*inTag=*/ TypeTag.UINT32, /*value=*/ 1234n, /*offset=*/ 1).execute( + machineState, + journal, + ); const actual = machineState.memory.get(1); const tag = machineState.memory.getTag(1); @@ -42,6 +132,61 @@ describe('Memory instructions', () => { }); describe('CAST', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Cast.opcode, + // indirect + 0x01, + // dstTag + TypeTag.FIELD, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst = Cast.deserialize(buf); + expect(inst).toEqual( + new Cast(/*indirect=*/ 0x01, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Cast( + /*indirect=*/ 0x01, + /*dstTag=*/ TypeTag.FIELD, + /*aOffset=*/ 0x12345678, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + Cast.opcode, + // indirect + 0x01, + // dstTag + TypeTag.FIELD, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should upcast between integral types', () => { machineState.memory.set(0, new Uint8(20n)); machineState.memory.set(1, new Uint16(65000n)); @@ -50,11 +195,11 @@ describe('Memory instructions', () => { machineState.memory.set(4, new Uint128(1n << 100n)); [ - new Cast(TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 10), - new Cast(TypeTag.UINT32, /*aOffset=*/ 1, /*dstOffset=*/ 11), - new Cast(TypeTag.UINT64, /*aOffset=*/ 2, /*dstOffset=*/ 12), - new Cast(TypeTag.UINT128, /*aOffset=*/ 3, /*dstOffset=*/ 13), - new Cast(TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT32, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); @@ -77,11 +222,11 @@ describe('Memory instructions', () => { machineState.memory.set(4, new Uint128((1n << 100n) - 1n)); [ - new Cast(TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), - new Cast(TypeTag.UINT8, /*aOffset=*/ 1, /*dstOffset=*/ 11), - new Cast(TypeTag.UINT16, /*aOffset=*/ 2, /*dstOffset=*/ 12), - new Cast(TypeTag.UINT32, /*aOffset=*/ 3, /*dstOffset=*/ 13), - new Cast(TypeTag.UINT64, /*aOffset=*/ 4, /*dstOffset=*/ 14), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT8, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT16, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT32, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 4, /*dstOffset=*/ 14), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); @@ -104,11 +249,11 @@ describe('Memory instructions', () => { machineState.memory.set(4, new Uint128(1n << 100n)); [ - new Cast(TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 10), - new Cast(TypeTag.FIELD, /*aOffset=*/ 1, /*dstOffset=*/ 11), - new Cast(TypeTag.FIELD, /*aOffset=*/ 2, /*dstOffset=*/ 12), - new Cast(TypeTag.FIELD, /*aOffset=*/ 3, /*dstOffset=*/ 13), - new Cast(TypeTag.FIELD, /*aOffset=*/ 4, /*dstOffset=*/ 14), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 4, /*dstOffset=*/ 14), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); @@ -131,11 +276,11 @@ describe('Memory instructions', () => { machineState.memory.set(4, new Field((1n << 200n) - 1n)); [ - new Cast(TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), - new Cast(TypeTag.UINT16, /*aOffset=*/ 1, /*dstOffset=*/ 11), - new Cast(TypeTag.UINT32, /*aOffset=*/ 2, /*dstOffset=*/ 12), - new Cast(TypeTag.UINT64, /*aOffset=*/ 3, /*dstOffset=*/ 13), - new Cast(TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT16, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT32, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT64, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), ].forEach(i => i.execute(machineState, journal)); const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); @@ -153,7 +298,10 @@ describe('Memory instructions', () => { it('Should cast between field elements', async () => { machineState.memory.set(0, new Field(12345678n)); - await new Cast(TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); + await new Cast(/*indirect=*/ 0, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute( + machineState, + journal, + ); const actual = machineState.memory.get(1); expect(actual).toEqual(new Field(12345678n)); @@ -163,23 +311,67 @@ describe('Memory instructions', () => { }); describe('MOV', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Mov.opcode, + // indirect + 0x01, + // srcOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst = Mov.deserialize(buf); + expect(inst).toEqual(new Mov(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a)); + }); + + it('Should serialize correctly', () => { + const inst = new Mov(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a); + + const expected = Buffer.from([ + // opcode + Mov.opcode, + // indirect + 0x01, + // srcOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should move integrals on different memory cells', async () => { - machineState.memory.set(1, new Uint16(27)); - await new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, journal); + machineState.memory.set(0, new Uint16(27)); + await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); - const actual = machineState.memory.get(2); - const tag = machineState.memory.getTag(2); + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); expect(actual).toEqual(new Uint16(27n)); expect(tag).toEqual(TypeTag.UINT16); }); it('Should move field elements on different memory cells', async () => { - machineState.memory.set(1, new Field(27)); - await new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, journal); + machineState.memory.set(0, new Field(27)); + await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); - const actual = machineState.memory.get(2); - const tag = machineState.memory.getTag(2); + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); expect(actual).toEqual(new Field(27n)); expect(tag).toEqual(TypeTag.FIELD); @@ -187,12 +379,90 @@ describe('Memory instructions', () => { }); describe('CMOV', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + CMov.opcode, + // indirect + 0x01, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // condOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst = CMov.deserialize(buf); + expect(inst).toEqual( + new CMov( + /*indirect=*/ 0x01, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0xa2345678, + /*condOffset=*/ 0xb2345678, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new CMov( + /*indirect=*/ 0x01, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0xa2345678, + /*condOffset=*/ 0xb2345678, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + CMov.opcode, + // indirect + 0x01, + // aOffset + 0x12, + 0x34, + 0x56, + 0x78, + // bOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // condOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should move A if COND is true, on different memory cells (integral condition)', async () => { machineState.memory.set(0, new Uint32(123)); // A machineState.memory.set(1, new Uint16(456)); // B machineState.memory.set(2, new Uint8(2)); // Condition - await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( machineState, journal, ); @@ -208,7 +478,7 @@ describe('Memory instructions', () => { machineState.memory.set(1, new Uint16(456)); // B machineState.memory.set(2, new Uint8(0)); // Condition - await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( machineState, journal, ); @@ -224,7 +494,7 @@ describe('Memory instructions', () => { machineState.memory.set(1, new Uint16(456)); // B machineState.memory.set(2, new Field(1)); // Condition - await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( machineState, journal, ); @@ -240,7 +510,7 @@ describe('Memory instructions', () => { machineState.memory.set(1, new Uint16(456)); // B machineState.memory.set(2, new Field(0)); // Condition - await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + await new CMov(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( machineState, journal, ); @@ -253,12 +523,81 @@ describe('Memory instructions', () => { }); describe('CALLDATACOPY', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + CalldataCopy.opcode, + // indirect + 0x01, + // cdOffset + 0x12, + 0x34, + 0x56, + 0x78, + // copysize + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + + const inst = CalldataCopy.deserialize(buf); + expect(inst).toEqual( + new CalldataCopy( + /*indirect=*/ 0x01, + /*cdOffset=*/ 0x12345678, + /*copysize=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new CalldataCopy( + /*indirect=*/ 0x01, + /*cdOffset=*/ 0x12345678, + /*copysize=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + const expected = Buffer.from([ + // opcode + CalldataCopy.opcode, + // indirect + 0x01, + // cdOffset + 0x12, + 0x34, + 0x56, + 0x78, + // copysize + 0x23, + 0x45, + 0x67, + 0x89, + // dstOffset + 0x34, + 0x56, + 0x78, + 0x9a, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Writes nothing if size is 0', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(machineState, journal); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute( + machineState, + journal, + ); const actual = machineState.memory.get(0); expect(actual).toEqual(new Uint16(12)); @@ -269,7 +608,10 @@ describe('Memory instructions', () => { machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(machineState, journal); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute( + machineState, + journal, + ); const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); expect(actual).toEqual([new Field(1), new Field(2), new Field(3)]); @@ -280,7 +622,10 @@ describe('Memory instructions', () => { machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - await new CalldataCopy(/*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(machineState, journal); + await new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute( + machineState, + journal, + ); const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); expect(actual).toEqual([new Field(2), new Field(3)]); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 2524684dc02..6868e1c32f3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,16 +1,44 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +import { TwoOperandInstruction } from './instruction_impl.js'; export class Set extends Instruction { - static type: string = 'SET'; - static numberOfOperands = 3; - - constructor(private inTag: TypeTag, private value: bigint, private dstOffset: number) { + static readonly type: string = 'SET'; + static readonly opcode: Opcode = Opcode.SET; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: Set) => Set.opcode, OperandType.UINT8], + [(c: Set) => c.indirect, OperandType.UINT8], + [(c: Set) => c.inTag, OperandType.UINT8], + [(c: Set) => c.value, OperandType.UINT128], + [(c: Set) => c.dstOffset, OperandType.UINT32], + ]; + + constructor(private indirect: number, private inTag: number, private value: bigint, private dstOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): Set { + const res = deserialize(buf, Set.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Set(...args); + } + + public serialize(): Buffer { + return serialize(Set.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const res = TaggedMemory.integralFromTag(this.value, this.inTag); @@ -20,72 +48,144 @@ export class Set extends Instruction { } } -export class Cast extends Instruction { - static type: string = 'CAST'; - static numberOfOperands = 3; - - constructor(private dstTag: TypeTag, private aOffset: number, private dstOffset: number) { +export class CMov extends Instruction { + static readonly type: string = 'CMOV'; + static readonly opcode: Opcode = Opcode.CMOV; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: CMov) => CMov.opcode, OperandType.UINT8], + [(c: CMov) => c.indirect, OperandType.UINT8], + [(c: CMov) => c.aOffset, OperandType.UINT32], + [(c: CMov) => c.bOffset, OperandType.UINT32], + [(c: CMov) => c.condOffset, OperandType.UINT32], + [(c: CMov) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + private indirect: number, + private aOffset: number, + private bOffset: number, + private condOffset: number, + private dstOffset: number, + ) { super(); } + public static deserialize(buf: BufferCursor | Buffer): CMov { + const res = deserialize(buf, CMov.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new CMov(...args); + } + + public serialize(): Buffer { + return serialize(CMov.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); + const cond = machineState.memory.get(this.condOffset); - // TODO: consider not using toBigInt() - const casted = - this.dstTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); - - machineState.memory.set(this.dstOffset, casted); + // TODO: reconsider toBigInt() here + machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); this.incrementPc(machineState); } } -export class Mov extends Instruction { - static type: string = 'MOV'; - static numberOfOperands = 2; +export class Cast extends TwoOperandInstruction { + static readonly type: string = 'CAST'; + static readonly opcode = Opcode.CAST; - constructor(private aOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, dstTag: number, aOffset: number, dstOffset: number) { + super(indirect, dstTag, aOffset, dstOffset); + } + + protected get opcode() { + return Cast.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): Cast { + const args = TwoOperandInstruction.deserializeBase(buf); + return new Cast(...args); } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); - machineState.memory.set(this.dstOffset, a); + // TODO: consider not using toBigInt() + const casted = + this.inTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.inTag); + + machineState.memory.set(this.dstOffset, casted); this.incrementPc(machineState); } } -export class CMov extends Instruction { - static type: string = 'CMOV'; - static numberOfOperands = 4; - - constructor(private aOffset: number, private bOffset: number, private condOffset: number, private dstOffset: number) { +export class Mov extends Instruction { + static readonly type: string = 'MOV'; + static readonly opcode: Opcode = Opcode.MOV; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: Mov) => Mov.opcode, OperandType.UINT8], + [(c: Mov) => c.indirect, OperandType.UINT8], + [(c: Mov) => c.srcOffset, OperandType.UINT32], + [(c: Mov) => c.dstOffset, OperandType.UINT32], + ]; + + constructor(private indirect: number, private srcOffset: number, private dstOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): Mov { + const res = deserialize(buf, Mov.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Mov(...args); + } + + public serialize(): Buffer { + return serialize(Mov.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const a = machineState.memory.get(this.aOffset); - const b = machineState.memory.get(this.bOffset); - const cond = machineState.memory.get(this.condOffset); + const a = machineState.memory.get(this.srcOffset); - // TODO: reconsider toBigInt() here - machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); + machineState.memory.set(this.dstOffset, a); this.incrementPc(machineState); } } export class CalldataCopy extends Instruction { - static type: string = 'CALLDATACOPY'; - static numberOfOperands = 3; - - constructor(private cdOffset: number, private copySize: number, private dstOffset: number) { + static readonly type: string = 'CALLDATACOPY'; + static readonly opcode: Opcode = Opcode.CALLDATACOPY; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: CalldataCopy) => CalldataCopy.opcode, OperandType.UINT8], + [(c: CalldataCopy) => c.indirect, OperandType.UINT8], + [(c: CalldataCopy) => c.cdOffset, OperandType.UINT32], + [(c: CalldataCopy) => c.copySize, OperandType.UINT32], + [(c: CalldataCopy) => c.dstOffset, OperandType.UINT32], + ]; + + constructor(private indirect: number, private cdOffset: number, private copySize: number, private dstOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): CalldataCopy { + const res = deserialize(buf, CalldataCopy.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new CalldataCopy(...args); + } + + public serialize(): Buffer { + return serialize(CalldataCopy.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const transformedData = machineState.executionEnvironment.calldata .slice(this.cdOffset, this.cdOffset + this.copySize) From 62d756117fd0cbaf79cad83f695acb622ff06402 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 12:03:20 +0000 Subject: [PATCH 07/25] storage instructions serialization --- .../src/avm/opcodes/storage.test.ts | 175 ++++++++++++++---- .../acir-simulator/src/avm/opcodes/storage.ts | 78 ++++++-- 2 files changed, 199 insertions(+), 54 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index e62b0abc561..dbfd4bf1296 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -21,48 +21,141 @@ describe('Storage Instructions', () => { machineState = new AvmMachineState(executionEnvironment); }); - it('Sstore should Write into storage', async () => { - const a = new Field(1n); - const b = new Field(2n); - - machineState.memory.set(0, a); - machineState.memory.set(1, b); - - await new SStore(0, 1).execute(machineState, journal); - - expect(journal.writeStorage).toBeCalledWith(address, new Fr(a.toBigInt()), new Fr(b.toBigInt())); - }); - - it('Should not be able to write to storage in a static call', async () => { - const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); - machineState = new AvmMachineState(executionEnvironment); - - const a = new Field(1n); - const b = new Field(2n); - - machineState.memory.set(0, a); - machineState.memory.set(1, b); - - const instruction = () => new SStore(0, 1).execute(machineState, journal); - await expect(instruction()).rejects.toThrowError(StaticCallStorageAlterError); + describe('SSTORE', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + SStore.opcode, + // indirect + 0x01, + // srcOffset + 0x12, + 0x34, + 0x56, + 0x78, + // slotOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = SStore.deserialize(buf); + expect(inst).toEqual(new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678)); + }); + + it('Should serialize correctly', () => { + const inst = new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678); + + const expected = Buffer.from([ + // opcode + SStore.opcode, + // indirect + 0x01, + // srcOffset + 0x12, + 0x34, + 0x56, + 0x78, + // slotOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Sstore should Write into storage', async () => { + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(machineState, journal); + + expect(journal.writeStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt()), new Fr(b.toBigInt())); + }); + + it('Should not be able to write to storage in a static call', async () => { + const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); + machineState = new AvmMachineState(executionEnvironment); + + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + const instruction = () => + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 1).execute(machineState, journal); + await expect(instruction()).rejects.toThrow(StaticCallStorageAlterError); + }); }); - it('Sload should Read into storage', async () => { - // Mock response - const expectedResult = new Fr(1n); - journal.readStorage.mockReturnValueOnce(Promise.resolve(expectedResult)); - - const a = new Field(1n); - const b = new Field(2n); - - machineState.memory.set(0, a); - machineState.memory.set(1, b); - - await new SLoad(0, 1).execute(machineState, journal); - - expect(journal.readStorage).toBeCalledWith(address, new Fr(a.toBigInt())); - - const actual = machineState.memory.get(1); - expect(actual).toEqual(new Field(expectedResult)); + describe('SLOAD', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + SLoad.opcode, + // indirect + 0x01, + // slotOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = SLoad.deserialize(buf); + expect(inst).toEqual(new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678)); + }); + + it('Should serialize correctly', () => { + const inst = new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678); + + const expected = Buffer.from([ + // opcode + SLoad.opcode, + // indirect + 0x01, + // slotOffset + 0x12, + 0x34, + 0x56, + 0x78, + // dstOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Sload should Read into storage', async () => { + // Mock response + const expectedResult = new Fr(1n); + journal.readStorage.mockReturnValueOnce(Promise.resolve(expectedResult)); + + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); + + expect(journal.readStorage).toHaveBeenCalledWith(address, new Fr(a.toBigInt())); + + const actual = machineState.memory.get(1); + expect(actual).toEqual(new Field(expectedResult)); + }); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index c226522b3db..c161513f310 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -4,23 +4,66 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmInterpreterError } from '../interpreter/interpreter.js'; import { AvmJournal } from '../journal/journal.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; -export class SStore extends Instruction { - static type: string = 'SSTORE'; - static numberOfOperands = 2; +abstract class BaseStorageInstruction extends Instruction { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: BaseStorageInstruction) => c.opcode, OperandType.UINT8], + [(c: BaseStorageInstruction) => c.indirect, OperandType.UINT8], + [(c: BaseStorageInstruction) => c.aOffset, OperandType.UINT32], + [(c: BaseStorageInstruction) => c.bOffset, OperandType.UINT32], + ]; - constructor(private slotOffset: number, private dataOffset: number) { + constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) { super(); } + protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { + const res = deserialize(buf, BaseStorageInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; + } + + public serialize(): Buffer { + return serialize(BaseStorageInstruction.wireFormat, this); + } + + protected abstract get opcode(): Opcode; +} + +export class SStore extends BaseStorageInstruction { + static readonly type: string = 'SSTORE'; + static readonly opcode = Opcode.SSTORE; + + constructor(indirect: number, srcOffset: number, slotOffset: number) { + super(indirect, srcOffset, slotOffset); + } + + protected get opcode() { + return SStore.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): SStore { + const args = BaseStorageInstruction.deserializeBase(buf); + return new SStore(...args); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); } - const slot = machineState.memory.get(this.slotOffset); - const data = machineState.memory.get(this.dataOffset); + const slot = machineState.memory.get(this.aOffset); + const data = machineState.memory.get(this.bOffset); journal.writeStorage( machineState.executionEnvironment.storageAddress, @@ -32,23 +75,32 @@ export class SStore extends Instruction { } } -export class SLoad extends Instruction { - static type: string = 'SLOAD'; - static numberOfOperands = 2; +export class SLoad extends BaseStorageInstruction { + static readonly type: string = 'SLOAD'; + static readonly opcode = Opcode.SLOAD; - constructor(private slotOffset: number, private dstOffset: number) { - super(); + constructor(indirect: number, slotOffset: number, dstOffset: number) { + super(indirect, slotOffset, dstOffset); + } + + protected get opcode() { + return SLoad.opcode; + } + + public static deserialize(buf: BufferCursor | Buffer): SLoad { + const args = BaseStorageInstruction.deserializeBase(buf); + return new SLoad(...args); } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { - const slot = machineState.memory.get(this.slotOffset); + const slot = machineState.memory.get(this.aOffset); const data: Fr = await journal.readStorage( machineState.executionEnvironment.storageAddress, new Fr(slot.toBigInt()), ); - machineState.memory.set(this.dstOffset, new Field(data)); + machineState.memory.set(this.bOffset, new Field(data)); this.incrementPc(machineState); } From ca83171506b9b9e5e2a3f3ec07a3422024d99216 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 13:08:10 +0000 Subject: [PATCH 08/25] control flow ops serialization --- .../src/avm/opcodes/control_flow.test.ts | 225 +++++++++++++++++- .../src/avm/opcodes/control_flow.ts | 136 +++++++++-- .../acir-simulator/src/avm/opcodes/index.ts | 1 + .../serialization/bytecode_serialization.ts | 152 ++++++------ 4 files changed, 420 insertions(+), 94 deletions(-) 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 index 1c5ac67d6eb..c1f87a41f99 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -18,7 +18,37 @@ describe('Control Flow Opcodes', () => { machineState = new AvmMachineState(initExecutionEnvironment()); }); - describe('Jumps', () => { + describe('JUMP', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Jump.opcode, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + ]); + + const inst: Jump = Jump.deserialize(buf); + expect(inst).toEqual(new Jump(/*loc=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Jump(/*loc=*/ 0x12345678); + + const expected = Buffer.from([ + // opcode + Jump.opcode, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should implement JUMP', async () => { const jumpLocation = 22; @@ -28,6 +58,52 @@ describe('Control Flow Opcodes', () => { await instruction.execute(machineState, journal); expect(machineState.pc).toBe(jumpLocation); }); + }); + + describe('JUMPI', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + JumpI.opcode, + // indirect + 0x01, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + // condOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + const inst: JumpI = JumpI.deserialize(buf); + expect(inst).toEqual(new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678)); + }); + + it('Should serialize correctly', () => { + const inst = new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678); + + const expected = Buffer.from([ + // opcode + JumpI.opcode, + // indirect + 0x01, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + // condOffset + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); it('Should implement JUMPI - truthy', async () => { const jumpLocation = 22; @@ -38,12 +114,12 @@ describe('Control Flow Opcodes', () => { machineState.memory.set(0, new Uint16(1n)); machineState.memory.set(1, new Uint16(2n)); - const instruction = new JumpI(jumpLocation, 0); + const instruction = new JumpI(/*indirect=*/ 0, jumpLocation, /*condOffset=*/ 0); await instruction.execute(machineState, journal); expect(machineState.pc).toBe(jumpLocation); // Truthy can be greater than 1 - const instruction1 = new JumpI(jumpLocation1, 1); + const instruction1 = new JumpI(/*indirect=*/ 0, jumpLocation1, /*condOffset=*/ 1); await instruction1.execute(machineState, journal); expect(machineState.pc).toBe(jumpLocation1); }); @@ -55,10 +131,43 @@ describe('Control Flow Opcodes', () => { machineState.memory.set(0, new Uint16(0n)); - const instruction = new JumpI(jumpLocation, 0); + const instruction = new JumpI(/*indirect=*/ 0, jumpLocation, /*condOffset=*/ 0); await instruction.execute(machineState, journal); expect(machineState.pc).toBe(1); }); + }); + + describe('INTERNALCALL and RETURN', () => { + it('INTERNALCALL should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + InternalCall.opcode, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + ]); + + const inst = InternalCall.deserialize(buf); + expect(inst).toEqual(new InternalCall(/*loc=*/ 0x12345678)); + }); + + it('INTERNALCALL should serialize correctly', () => { + const inst = new InternalCall(/*loc=*/ 0x12345678); + + const expected = Buffer.from([ + // opcode + InternalCall.opcode, + // loc + 0x12, + 0x34, + 0x56, + 0x78, + ]); + + expect(inst.serialize()).toEqual(expected); + }); it('Should implement Internal Call and Return', async () => { const jumpLocation = 22; @@ -75,6 +184,13 @@ describe('Control Flow Opcodes', () => { expect(machineState.pc).toBe(1); }); + it('Should error if Internal Return is called without a corresponding Internal Call', async () => { + const returnInstruction = () => new InternalReturn().execute(machineState, journal); + await expect(returnInstruction()).rejects.toThrow(InstructionExecutionError); + }); + }); + + describe('General flow', () => { it('Should chain series of control flow instructions', async () => { const jumpLocation0 = 22; const jumpLocation1 = 69; @@ -109,14 +225,54 @@ describe('Control Flow Opcodes', () => { expect(machineState.pc).toBe(expectedPcs[i]); } }); + }); - it('Should error if Internal Return is called without a corresponding Internal Call', async () => { - const returnInstruction = () => new InternalReturn().execute(machineState, journal); - await expect(returnInstruction()).rejects.toThrow(InstructionExecutionError); + describe('RETURN', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Return.opcode, + // indirect + 0x01, + // returnOffset + 0x12, + 0x34, + 0x56, + 0x78, + // copySize + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = Return.deserialize(buf); + expect(inst).toEqual(new Return(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*copySize=*/ 0xa2345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Return(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*copySize=*/ 0xa2345678); + + const expected = Buffer.from([ + // opcode + Return.opcode, + // indirect + 0x01, + // returnOffset + 0x12, + 0x34, + 0x56, + 0x78, + // copySize + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + expect(inst.serialize()).toEqual(expected); }); - }); - describe('Halting Opcodes', () => { it('Should return data from the return opcode', async () => { const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; @@ -124,13 +280,60 @@ describe('Control Flow Opcodes', () => { machineState.memory.set(1, new Field(2n)); machineState.memory.set(2, new Field(3n)); - const instruction = new Return(0, returnData.length); + const instruction = new Return(/*indirect=*/ 0, /*returnOffset=*/ 0, returnData.length); await instruction.execute(machineState, journal); expect(machineState.getReturnData()).toEqual(returnData); expect(machineState.halted).toBe(true); expect(machineState.reverted).toBe(false); }); + }); + + describe('REVERT', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Revert.opcode, + // indirect + 0x01, + // returnOffset + 0x12, + 0x34, + 0x56, + 0x78, + // retSize + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = Revert.deserialize(buf); + expect(inst).toEqual(new Revert(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*retSize=*/ 0xa2345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Revert(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*retSize=*/ 0xa2345678); + + const expected = Buffer.from([ + // opcode + Revert.opcode, + // indirect + 0x01, + // returnOffset + 0x12, + 0x34, + 0x56, + 0x78, + // retSize + 0xa2, + 0x34, + 0x56, + 0x78, + ]); + + expect(inst.serialize()).toEqual(expected); + }); it('Should return data and revert from the revert opcode', async () => { const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; @@ -139,7 +342,7 @@ describe('Control Flow Opcodes', () => { machineState.memory.set(1, new Field(2n)); machineState.memory.set(2, new Field(3n)); - const instruction = new Revert(0, returnData.length); + const instruction = new Revert(/*indirect=*/ 0, /*returnOffset=*/ 0, returnData.length); await instruction.execute(machineState, journal); expect(machineState.getReturnData()).toEqual(returnData); 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 c890fc700e3..bf46f56dbba 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,16 +1,42 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { IntegralValue } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; export class Return extends Instruction { static type: string = 'RETURN'; - static numberOfOperands = 2; + static readonly opcode: Opcode = Opcode.RETURN; - constructor(private returnOffset: number, private copySize: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: Return) => Return.opcode, OperandType.UINT8], + [(c: Return) => c.indirect, OperandType.UINT8], + [(c: Return) => c.returnOffset, OperandType.UINT32], + [(c: Return) => c.copySize, OperandType.UINT32], + ]; + + constructor(private indirect: number, private returnOffset: number, private copySize: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): Return { + const res = deserialize(buf, Return.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Return(...args); + } + + public serialize(): Buffer { + return serialize(Return.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); @@ -22,12 +48,30 @@ export class Return extends Instruction { export class Revert extends Instruction { static type: string = 'RETURN'; - static numberOfOperands = 2; + static readonly opcode: Opcode = Opcode.REVERT; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: Revert) => Revert.opcode, OperandType.UINT8], + [(c: Revert) => c.indirect, OperandType.UINT8], + [(c: Revert) => c.returnOffset, OperandType.UINT32], + [(c: Revert) => c.retSize, OperandType.UINT32], + ]; - constructor(private returnOffset: number, private retSize: number) { + constructor(private indirect: number, private returnOffset: number, private retSize: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): Revert { + const res = deserialize(buf, Revert.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Revert(...args); + } + + public serialize(): Buffer { + return serialize(Revert.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory .getSlice(this.returnOffset, this.returnOffset + this.retSize) @@ -40,12 +84,28 @@ export class Revert extends Instruction { export class Jump extends Instruction { static type: string = 'JUMP'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.JUMP; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: Jump) => Jump.opcode, OperandType.UINT8], + [(c: Jump) => c.jumpOffset, OperandType.UINT32], + ]; constructor(private jumpOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): Jump { + const res = deserialize(buf, Jump.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Jump(...args); + } + + public serialize(): Buffer { + return serialize(Jump.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.pc = this.jumpOffset; } @@ -53,12 +113,30 @@ export class Jump extends Instruction { export class JumpI extends Instruction { static type: string = 'JUMPI'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.JUMPI; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: JumpI) => JumpI.opcode, OperandType.UINT8], + [(c: JumpI) => c.indirect, OperandType.UINT8], + [(c: JumpI) => c.loc, OperandType.UINT32], + [(c: JumpI) => c.condOffset, OperandType.UINT32], + ]; - constructor(private jumpOffset: number, private condOffset: number) { + constructor(private indirect: number, private loc: number, private condOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): JumpI { + const res = deserialize(buf, JumpI.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new JumpI(...args); + } + + public serialize(): Buffer { + return serialize(JumpI.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const condition = machineState.memory.getAs(this.condOffset); @@ -66,33 +144,63 @@ export class JumpI extends Instruction { if (condition.toBigInt() == 0n) { this.incrementPc(machineState); } else { - machineState.pc = this.jumpOffset; + machineState.pc = this.loc; } } } export class InternalCall extends Instruction { - static type: string = 'INTERNALCALL'; - static numberOfOperands = 1; + static readonly type: string = 'INTERNALCALL'; + static readonly opcode: Opcode = Opcode.INTERNALCALL; - constructor(private jumpOffset: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: InternalCall) => InternalCall.opcode, OperandType.UINT8], + [(c: InternalCall) => c.loc, OperandType.UINT32], + ]; + + constructor(private loc: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): InternalCall { + const res = deserialize(buf, InternalCall.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new InternalCall(...args); + } + + public serialize(): Buffer { + return serialize(InternalCall.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.internalCallStack.push(machineState.pc + 1); - machineState.pc = this.jumpOffset; + machineState.pc = this.loc; } } export class InternalReturn extends Instruction { - static type: string = 'INTERNALRETURN'; - static numberOfOperands = 0; + static readonly type: string = 'INTERNALRETURN'; + static readonly opcode: Opcode = Opcode.INTERNALRETURN; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_c: InternalReturn) => InternalReturn.opcode, OperandType.UINT8], + ]; constructor() { super(); } + public static deserialize(buf: BufferCursor | Buffer): InternalReturn { + deserialize(buf, InternalReturn.wireFormat); // only contains opcode. + return new InternalReturn(); + } + + public serialize(): Buffer { + return serialize(InternalReturn.wireFormat, this); + } + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const jumpOffset = machineState.internalCallStack.pop(); if (jumpOffset === undefined) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index 10ce6b4d0f7..aaf99d3393a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -1,4 +1,5 @@ export * from './arithmetic.js'; +export * from './bitwise.js'; export * from './control_flow.js'; export * from './instruction.js'; export * from './comparators.js'; diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index aa56e785de4..5ab41ca9d7b 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -1,108 +1,122 @@ -import { BufferCursor } from './buffer_cursor.js'; -import { Add, Sub } from '../opcodes/index.js'; +import { + Add, + And, + CMov, + CalldataCopy, + Cast, + Div, + Eq, + Lt, + Lte, + Mov, + Mul, + Not, + Or, + Set, + Shl, + Shr, + Sub, + Xor, +} from '../opcodes/index.js'; import { Instruction } from '../opcodes/instruction.js'; +import { BufferCursor } from './buffer_cursor.js'; import { Opcode } from './instruction_serialization.js'; -export interface DeserializableInstruction { - deserialize(buf: BufferCursor): Instruction; +interface DeserializableInstruction { + deserialize(buf: BufferCursor | Buffer): Instruction; opcode: Opcode; - // serialize(this: any): Buffer; } export type InstructionSet = Map; const INSTRUCTION_SET: InstructionSet = new Map( [ - // new Array<[Opcode, InstructionConstructorAndMembers]>( - // Compute - // Compute - Arithmetic [Add.opcode, Add], [Sub.opcode, Sub], - // [Opcode.SUB, Sub], - // [Opcode.MUL, Mul], - // [Opcode.DIV, Div], - // //// Compute - Comparators - // //[Opcode.EQ, Eq], - // //[Opcode.LT, Lt], - // //[Opcode.LTE, Lte], - // //// Compute - Bitwise - // [Opcode.AND, And], - // [Opcode.OR, Or], - // [Opcode.XOR, Xor], - // [Opcode.NOT, Not], - // [Opcode.SHL, Shl], - // [Opcode.SHR, Shr], - // //// Compute - Type Conversions - // [Opcode.CAST, Cast], - + [Sub.opcode, Sub], + [Mul.opcode, Mul], + [Div.opcode, Div], + [Eq.opcode, Eq], + [Lt.opcode, Lt], + [Lte.opcode, Lte], + [And.opcode, And], + [Or.opcode, Or], + [Xor.opcode, Xor], + [Not.opcode, Not], + [Shl.opcode, Shl], + [Shr.opcode, Shr], + [Cast.opcode, Cast], // //// Execution Environment - // //[Opcode.ADDRESS, Address], - // //[Opcode.STORAGEADDRESS, Storageaddress], - // //[Opcode.ORIGIN, Origin], - // //[Opcode.SENDER, Sender], - // //[Opcode.PORTAL, Portal], - // //[Opcode.FEEPERL1GAS, Feeperl1gas], - // //[Opcode.FEEPERL2GAS, Feeperl2gas], - // //[Opcode.FEEPERDAGAS, Feeperdagas], - // //[Opcode.CONTRACTCALLDEPTH, Contractcalldepth], + // //[Address.opcode, Address], + // //[Storageaddress.opcode, Storageaddress], + // //[Origin.opcode, Origin], + // //[Sender.opcode, Sender], + // //[Portal.opcode, Portal], + // //[Feeperl1gas.opcode, Feeperl1gas], + // //[Feeperl2gas.opcode, Feeperl2gas], + // //[Feeperdagas.opcode, Feeperdagas], + // //[Contractcalldepth.opcode, Contractcalldepth], // //// Execution Environment - Globals - // //[Opcode.CHAINID, Chainid], - // //[Opcode.VERSION, Version], - // //[Opcode.BLOCKNUMBER, Blocknumber], - // //[Opcode.TIMESTAMP, Timestamp], - // //[Opcode.COINBASE, Coinbase], - // //[Opcode.BLOCKL1GASLIMIT, Blockl1gaslimit], - // //[Opcode.BLOCKL2GASLIMIT, Blockl2gaslimit], - // //[Opcode.BLOCKDAGASLIMIT, Blockdagaslimit], + // //[Chainid.opcode, Chainid], + // //[Version.opcode, Version], + // //[Blocknumber.opcode, Blocknumber], + // //[Timestamp.opcode, Timestamp], + // //[Coinbase.opcode, Coinbase], + // //[Blockl1gaslimit.opcode, Blockl1gaslimit], + // //[Blockl2gaslimit.opcode, Blockl2gaslimit], + // //[Blockdagaslimit.opcode, Blockdagaslimit], // // Execution Environment - Calldata - // [Opcode.CALLDATACOPY, CalldataCopy], + [CalldataCopy.opcode, CalldataCopy], // //// Machine State // // Machine State - Gas - // //[Opcode.L1GASLEFT, L1gasleft], - // //[Opcode.L2GASLEFT, L2gasleft], - // //[Opcode.DAGASLEFT, Dagasleft], + // //[L1gasleft.opcode, L1gasleft], + // //[L2gasleft.opcode, L2gasleft], + // //[Dagasleft.opcode, Dagasleft], // //// Machine State - Internal Control Flow - // [Opcode.JUMP, Jump], - // [Opcode.JUMPI, JumpI], - // [Opcode.INTERNALCALL, InternalCall], - // [Opcode.INTERNALRETURN, InternalReturn], + // [Jump.opcode, Jump], + // [JumpI.opcode, JumpI], + // [InternalCall.opcode, InternalCall], + // [InternalReturn.opcode, InternalReturn], // //// Machine State - Memory - // [Opcode.SET, Set], - // [Opcode.MOV, Mov], - // [Opcode.CMOV, CMov], + [Set.opcode, Set], + [Mov.opcode, Mov], + [CMov.opcode, CMov], // //// World State - // //[Opcode.BLOCKHEADERBYNUMBER, Blockheaderbynumber], - // [Opcode.SLOAD, SLoad], // Public Storage - // [Opcode.SSTORE, SStore], // Public Storage - // //[Opcode.READL1TOL2MSG, Readl1tol2msg], // Messages - // //[Opcode.SENDL2TOL1MSG, Sendl2tol1msg], // Messages - // //[Opcode.EMITNOTEHASH, Emitnotehash], // Notes & Nullifiers - // //[Opcode.EMITNULLIFIER, Emitnullifier], // Notes & Nullifiers + // //[Blockheaderbynumber.opcode, Blockheaderbynumber], + // [SLoad.opcode, SLoad], // Public Storage + // [SStore.opcode, SStore], // Public Storage + // //[Readl1tol2msg.opcode, Readl1tol2msg], // Messages + // //[Sendl2tol1msg.opcode, Sendl2tol1msg], // Messages + // //[Emitnotehash.opcode, Emitnotehash], // Notes & Nullifiers + // //[Emitnullifier.opcode, Emitnullifier], // Notes & Nullifiers // //// Accrued Substate - // //[Opcode.EMITUNENCRYPTEDLOG, Emitunencryptedlog], + // //[Emitunencryptedlog.opcode, Emitunencryptedlog], // //// Control Flow - Contract Calls - // // [Opcode.CALL, Call], - // //[Opcode.STATICCALL, Staticcall], - // [Opcode.RETURN, Return], - // //[Opcode.REVERT, Revert], + // // [Call.opcode, Call], + // //[Staticcall.opcode, Staticcall], + // [Return.opcode, Return], + // //[Revert.opcode, Revert], // //// Gadgets - // //[Opcode.KECCAK, Keccak], - // //[Opcode.POSEIDON, Poseidon], + // //[Keccak.opcode, Keccak], + // //[Poseidon.opcode, Poseidon], ], //), ); +interface Serializable { + serialize(): Buffer; +} + /** * TODO: doc * @param opcode - the opcode to encode * @param args - the arguments to encode * @returns the bytecode for this one instruction */ -// FIXME: instructions: any[] -export function encodeToBytecode(instructions: any[]): Buffer { +export function encodeToBytecode(instructions: Serializable[]): Buffer { return Buffer.concat(instructions.map(i => i.serialize())); } From 5263d8cf806255209856b21ab296db2241d18365 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 15:15:25 +0000 Subject: [PATCH 09/25] external calls serialization --- .../acir-simulator/src/avm/avm_context.ts | 4 +- .../src/avm/interpreter/interpreter.ts | 11 +- .../src/avm/opcodes/external_calls.test.ts | 293 ++++++++++++++++-- .../src/avm/opcodes/external_calls.ts | 72 ++++- .../acir-simulator/src/avm/opcodes/index.ts | 1 + .../acir-simulator/src/avm/opcodes/storage.ts | 5 +- .../src/avm/serialization/buffer_cursor.ts | 4 - .../bytecode_serialization.test.ts | 39 ++- .../serialization/bytecode_serialization.ts | 27 +- .../instruction_serialization.test.ts | 6 + 10 files changed, 398 insertions(+), 64 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 0fcb86cfde6..710f30ef63d 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -6,8 +6,8 @@ import { AvmMachineState } from './avm_machine_state.js'; import { AvmMessageCallResult } from './avm_message_call_result.js'; import { AvmInterpreterError, executeAvm } from './interpreter/index.js'; import { AvmJournal } from './journal/journal.js'; -import { decodeBytecode } from './opcodes/decode_bytecode.js'; import { Instruction } from './opcodes/index.js'; +import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; /** * Avm Executor manages the execution of the AVM @@ -47,7 +47,7 @@ export class AvmContext { throw new NoBytecodeFoundInterpreterError(this.executionEnvironment.address); } - const instructions: Instruction[] = decodeBytecode(bytecode); + const instructions: Instruction[] = decodeFromBytecode(bytecode); const machineState = new AvmMachineState(this.executionEnvironment); return executeAvm(machineState, this.journal, instructions); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts index 86385078cac..fb23425f8b8 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts @@ -3,7 +3,7 @@ import { strict as assert } from 'assert'; import { AvmMachineState } from '../avm_machine_state.js'; import { AvmMessageCallResult } from '../avm_message_call_result.js'; import { AvmJournal } from '../journal/index.js'; -import { Instruction } from '../opcodes/index.js'; +import { Instruction, InstructionExecutionError } from '../opcodes/instruction.js'; /** * Run the avm @@ -36,14 +36,13 @@ export async function executeAvm( } return AvmMessageCallResult.success(returnData); - } catch (_e) { - if (!(_e instanceof AvmInterpreterError)) { - throw _e; + } catch (e) { + if (!(e instanceof AvmInterpreterError || e instanceof InstructionExecutionError)) { + throw e; } - const revertReason: AvmInterpreterError = _e; const revertData = machineState.getReturnData(); - return AvmMessageCallResult.revert(revertData, revertReason); + return AvmMessageCallResult.revert(revertData, /*revertReason=*/ e); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index 1d91c3bc405..8f34d9fa951 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -9,9 +9,12 @@ import { Field } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { HostStorage } from '../journal/host_storage.js'; import { AvmJournal } from '../journal/journal.js'; -import { encodeToBytecode } from './encode_to_bytecode.js'; -import { Call } from './external_calls.js'; -import { Opcode } from './opcodes.js'; +import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; +import { Return } from './control_flow.js'; +import { Call, StaticCall } from './external_calls.js'; +import { Instruction } from './instruction.js'; +import { CalldataCopy } from './memory.js'; +import { SStore } from './storage.js'; describe('External Calls', () => { let machineState: AvmMachineState; @@ -31,44 +34,155 @@ describe('External Calls', () => { }); describe('Call', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Call.opcode, + // indirect + 0x01, + // gasOffset + 0x12, + 0x34, + 0x56, + 0x78, + // addrOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // argsOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // argsSize + 0xc2, + 0x34, + 0x56, + 0x78, + // retOffset + 0xd2, + 0x34, + 0x56, + 0x78, + // retSize + 0xe2, + 0x34, + 0x56, + 0x78, + // successOffset + 0xf2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = Call.deserialize(buf); + expect(inst).toEqual( + new Call( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new Call( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ); + + const expected = Buffer.from([ + // opcode + Call.opcode, + // indirect + 0x01, + // gasOffset + 0x12, + 0x34, + 0x56, + 0x78, + // addrOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // argsOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // argsSize + 0xc2, + 0x34, + 0x56, + 0x78, + // retOffset + 0xd2, + 0x34, + 0x56, + 0x78, + // retSize + 0xe2, + 0x34, + 0x56, + 0x78, + // successOffset + 0xf2, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): gas not implemented it('Should execute a call correctly', async () => { const gasOffset = 0; const gas = Fr.zero(); - const addrOffset = 1; const addr = new Fr(123456n); - const argsOffset = 2; const args = [new Field(1n), new Field(2n), new Field(3n)]; const argsSize = args.length; - const retOffset = 8; const retSize = 2; - const successOffset = 7; + const otherContextInstructionsBytecode = encodeToBytecode([ + new CalldataCopy(/*indirect=*/ 0, /*csOffset=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0), + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 0, /*slotOffset=*/ 0), + new Return(/*indirect=*/ 0, /*retOffset=*/ 0, /*size=*/ 2), + ]); machineState.memory.set(0, new Field(gas)); machineState.memory.set(1, new Field(addr)); machineState.memory.setSlice(2, args); - - const otherContextInstructions: [Opcode, any[]][] = [ - // Place [1,2,3] into memory - [Opcode.CALLDATACOPY, [/*value=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0]], - // Store 1 into slot 1 - [Opcode.SSTORE, [/*slotOffset=*/ 0, /*dataOffset=*/ 0]], - // Return [1,2] from memory - [Opcode.RETURN, [/*retOffset=*/ 0, /*size=*/ 2]], - ]; - - const otherContextInstructionsBytecode = Buffer.concat( - otherContextInstructions.map(([opcode, args]) => encodeToBytecode(opcode, args)), - ); jest .spyOn(journal.hostStorage.contractsDb, 'getBytecode') .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); - const instruction = new Call(gasOffset, addrOffset, argsOffset, argsSize, retOffset, retSize, successOffset); + const instruction = new Call( + /*indirect=*/ 0, + gasOffset, + addrOffset, + argsOffset, + argsSize, + retOffset, + retSize, + successOffset, + ); await instruction.execute(machineState, journal); const successValue = machineState.memory.get(successOffset); @@ -91,6 +205,120 @@ describe('External Calls', () => { }); describe('Static Call', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + StaticCall.opcode, + // indirect + 0x01, + // gasOffset + 0x12, + 0x34, + 0x56, + 0x78, + // addrOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // argsOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // argsSize + 0xc2, + 0x34, + 0x56, + 0x78, + // retOffset + 0xd2, + 0x34, + 0x56, + 0x78, + // retSize + 0xe2, + 0x34, + 0x56, + 0x78, + // successOffset + 0xf2, + 0x34, + 0x56, + 0x78, + ]); + + const inst = StaticCall.deserialize(buf); + expect(inst).toEqual( + new StaticCall( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ), + ); + }); + + it('Should serialize correctly', () => { + const inst = new StaticCall( + /*indirect=*/ 0x01, + /*gasOffset=*/ 0x12345678, + /*addrOffset=*/ 0xa2345678, + /*argsOffset=*/ 0xb2345678, + /*argsSize=*/ 0xc2345678, + /*retOffset=*/ 0xd2345678, + /*retSize=*/ 0xe2345678, + /*successOffset=*/ 0xf2345678, + ); + + const expected = Buffer.from([ + // opcode + StaticCall.opcode, + // indirect + 0x01, + // gasOffset + 0x12, + 0x34, + 0x56, + 0x78, + // addrOffset + 0xa2, + 0x34, + 0x56, + 0x78, + // argsOffset + 0xb2, + 0x34, + 0x56, + 0x78, + // argsSize + 0xc2, + 0x34, + 0x56, + 0x78, + // retOffset + 0xd2, + 0x34, + 0x56, + 0x78, + // retSize + 0xe2, + 0x34, + 0x56, + 0x78, + // successOffset + 0xf2, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + it('Should fail if a static call attempts to touch storage', async () => { const gasOffset = 0; const gas = new Field(0); @@ -108,20 +336,27 @@ describe('External Calls', () => { machineState.memory.set(1, addr); machineState.memory.setSlice(2, args); - const otherContextInstructions: [Opcode, any[]][] = [ - // Place [1,2,3] into memory - [Opcode.CALLDATACOPY, [/*value=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0]], - [Opcode.SSTORE, [/*slotOffset*/ 1, /*dataOffset=*/ 0]], + const otherContextInstructions: Instruction[] = [ + new CalldataCopy(/*indirect=*/ 0, /*csOffset=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0), + new SStore(/*indirect=*/ 0, /*srcOffset=*/ 1, /*slotOffset=*/ 0), ]; - const otherContextInstructionsBytecode = Buffer.concat( - otherContextInstructions.map(([opcode, args]) => encodeToBytecode(opcode, args)), - ); + const otherContextInstructionsBytecode = encodeToBytecode(otherContextInstructions); + jest .spyOn(journal.hostStorage.contractsDb, 'getBytecode') .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); - const instruction = new Call(gasOffset, addrOffset, argsOffset, argsSize, retOffset, retSize, successOffset); + const instruction = new StaticCall( + /*indirect=*/ 0, + gasOffset, + addrOffset, + argsOffset, + argsSize, + retOffset, + retSize, + successOffset, + ); await instruction.execute(machineState, journal); // No revert has occurred, but the nested execution has failed diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 280b1284f02..913300f5088 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -4,17 +4,39 @@ import { AvmContext } from '../avm_context.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; export class Call extends Instruction { static type: string = 'CALL'; - static numberOfOperands = 7; + static readonly opcode: Opcode = Opcode.CALL; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: Call) => Call.opcode, OperandType.UINT8], + [(c: Call) => c.indirect, OperandType.UINT8], + [(c: Call) => c._gasOffset, OperandType.UINT32], + [(c: Call) => c.addrOffset, OperandType.UINT32], + [(c: Call) => c.argsOffset, OperandType.UINT32], + [(c: Call) => c.argsSize, OperandType.UINT32], + [(c: Call) => c.retOffset, OperandType.UINT32], + [(c: Call) => c.retSize, OperandType.UINT32], + [(c: Call) => c.successOffset, OperandType.UINT32], + ]; constructor( - private /* Unused due to no formal gas implementation at this moment */ _gasOffset: number, + private indirect: number, + private _gasOffset: number /* Unused due to no formal gas implementation at this moment */, private addrOffset: number, private argsOffset: number, - private argSize: number, + private argsSize: number, private retOffset: number, private retSize: number, private successOffset: number, @@ -22,10 +44,20 @@ export class Call extends Instruction { super(); } + public static deserialize(buf: BufferCursor | Buffer): Call { + const res = deserialize(buf, Call.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new Call(...args); + } + + public serialize(): Buffer { + return serialize(Call.wireFormat, this); + } + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const callAddress = machineState.memory.getAs(this.addrOffset); - const calldata = machineState.memory.getSlice(this.argsOffset, this.argSize).map(f => new Fr(f.toBigInt())); + const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); const avmContext = AvmContext.prepExternalCallContext( new Fr(callAddress.toBigInt()), @@ -55,13 +87,27 @@ export class Call extends Instruction { export class StaticCall extends Instruction { static type: string = 'STATICCALL'; - static numberOfOperands = 7; + static readonly opcode: Opcode = Opcode.STATICCALL; + + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: StaticCall) => StaticCall.opcode, OperandType.UINT8], + [(c: StaticCall) => c.indirect, OperandType.UINT8], + [(c: StaticCall) => c._gasOffset, OperandType.UINT32], + [(c: StaticCall) => c.addrOffset, OperandType.UINT32], + [(c: StaticCall) => c.argsOffset, OperandType.UINT32], + [(c: StaticCall) => c.argsSize, OperandType.UINT32], + [(c: StaticCall) => c.retOffset, OperandType.UINT32], + [(c: StaticCall) => c.retSize, OperandType.UINT32], + [(c: StaticCall) => c.successOffset, OperandType.UINT32], + ]; constructor( - private /* Unused due to no formal gas implementation at this moment */ _gasOffset: number, + private indirect: number, + private _gasOffset: number /* Unused due to no formal gas implementation at this moment */, private addrOffset: number, private argsOffset: number, - private argSize: number, + private argsSize: number, private retOffset: number, private retSize: number, private successOffset: number, @@ -69,9 +115,19 @@ export class StaticCall extends Instruction { super(); } + public static deserialize(buf: BufferCursor | Buffer): StaticCall { + const res = deserialize(buf, StaticCall.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new StaticCall(...args); + } + + public serialize(): Buffer { + return serialize(StaticCall.wireFormat, this); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const callAddress = machineState.memory.get(this.addrOffset); - const calldata = machineState.memory.getSlice(this.argsOffset, this.argSize).map(f => new Fr(f.toBigInt())); + const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); const avmContext = AvmContext.prepExternalStaticCallContext( new Fr(callAddress.toBigInt()), diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index aaf99d3393a..1c81233cb7d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -4,3 +4,4 @@ export * from './control_flow.js'; export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; +export * from './storage.js'; \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index c161513f310..b75e2c9ddf6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -2,7 +2,6 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; -import { AvmInterpreterError } from '../interpreter/interpreter.js'; import { AvmJournal } from '../journal/journal.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { @@ -12,7 +11,7 @@ import { deserialize, serialize, } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; +import { Instruction, InstructionExecutionError } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { // Instruction wire format with opcode. @@ -109,7 +108,7 @@ export class SLoad extends BaseStorageInstruction { /** * Error is thrown when a static call attempts to alter storage */ -export class StaticCallStorageAlterError extends AvmInterpreterError { +export class StaticCallStorageAlterError extends InstructionExecutionError { constructor() { super('Static calls cannot alter storage'); this.name = 'StaticCallStorageAlterError'; diff --git a/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts index 6229d2a1a8f..aab7bf59e45 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts @@ -14,10 +14,6 @@ export class BufferCursor { return this._position === this._buffer.length; } - public buffer(): Buffer { - return this._buffer; - } - public bufferAtPosition(): Buffer { return this._buffer.subarray(this._position); } diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts index 469816b3753..145ad283910 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts @@ -1,3 +1,6 @@ +import { strict as assert } from 'assert'; + +import { Add, Sub } from '../opcodes/index.js'; import { BufferCursor } from './buffer_cursor.js'; import { InstructionSet, decodeFromBytecode, encodeToBytecode } from './bytecode_serialization.js'; import { Opcode } from './instruction_serialization.js'; @@ -6,10 +9,14 @@ class InstA { constructor(private n: number) {} static readonly opcode: number = 1; + // Expects opcode. public static deserialize(buf: BufferCursor): InstA { + const opcode: number = buf.readUint8(); + assert(opcode == InstA.opcode); return new InstA(buf.readUint16BE()); } + // Includes opcode. public serialize(): Buffer { const buf = Buffer.alloc(1 + 2); buf.writeUint8(InstA.opcode); @@ -22,10 +29,14 @@ class InstB { constructor(private n: bigint) {} static readonly opcode: number = 2; + // Expects opcode. public static deserialize(buf: BufferCursor): InstB { + const opcode: number = buf.readUint8(); + assert(opcode == InstB.opcode); return new InstB(buf.readBigInt64BE()); } + // Includes opcode. public serialize(): Buffer { const buf = Buffer.alloc(1 + 8); buf.writeUint8(InstB.opcode); @@ -40,8 +51,8 @@ describe('Bytecode Serialization', () => { [InstA.opcode, InstA], [InstB.opcode, InstB], ]); - const a = new InstA(1234); - const b = new InstB(5678n); + const a = new InstA(0x1234); + const b = new InstB(0x5678n); const bytecode = Buffer.concat([a.serialize(), b.serialize()]); const actual = decodeFromBytecode(bytecode, instructionSet); @@ -58,4 +69,28 @@ describe('Bytecode Serialization', () => { const expected = Buffer.concat([a.serialize(), b.serialize()]); expect(actual).toEqual(expected); }); + + it('Should deserialize real instructions', () => { + const instructions = [ + new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + ]; + const bytecode = Buffer.concat(instructions.map(i => i.serialize())); + + const actual = decodeFromBytecode(bytecode); + + expect(actual).toEqual(instructions); + }); + + it('Should serialize real instructions', () => { + const instructions = [ + new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + ]; + + const actual = encodeToBytecode(instructions); + + const expected = Buffer.concat(instructions.map(i => i.serialize())); + expect(actual).toEqual(expected); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index 5ab41ca9d7b..bfb4150caf3 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -6,12 +6,20 @@ import { Cast, Div, Eq, + InternalCall, + InternalReturn, + Jump, + JumpI, Lt, Lte, Mov, Mul, Not, Or, + Return, + Revert, + SLoad, + SStore, Set, Shl, Shr, @@ -73,19 +81,18 @@ const INSTRUCTION_SET: InstructionSet = new Map InstA.opcode, OperandType.UINT8], [(c: InstA) => c.a, OperandType.UINT8], [(c: InstA) => c.b, OperandType.UINT16], [(c: InstA) => c.c, OperandType.UINT32], @@ -22,6 +24,8 @@ describe('Instruction Serialization', () => { expect(actual).toEqual( Buffer.from( [ + // opcode + '01', // a '12', // b @@ -41,6 +45,8 @@ describe('Instruction Serialization', () => { it('Should deserialize all types from OperandPair[]', () => { const buffer = Buffer.from( [ + // opcode + '01', // a '12', // b From b6b35ac49217058fc88d888251436cbd252b2df9 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 31 Jan 2024 15:28:40 +0000 Subject: [PATCH 10/25] avmcontext dependency cycle --- yarn-project/acir-simulator/src/avm/avm_context.ts | 4 ++-- yarn-project/acir-simulator/src/avm/opcodes/index.ts | 4 +++- .../src/avm/serialization/bytecode_serialization.ts | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 710f30ef63d..f92d040ff6e 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -6,8 +6,8 @@ import { AvmMachineState } from './avm_machine_state.js'; import { AvmMessageCallResult } from './avm_message_call_result.js'; import { AvmInterpreterError, executeAvm } from './interpreter/index.js'; import { AvmJournal } from './journal/journal.js'; -import { Instruction } from './opcodes/index.js'; -import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; +import { Instruction } from './opcodes/instruction.js'; +import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; // FIXME: dependency cycle. /** * Avm Executor manages the execution of the AVM diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index 1c81233cb7d..8f84f04e30c 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -4,4 +4,6 @@ export * from './control_flow.js'; export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; -export * from './storage.js'; \ No newline at end of file +export * from './storage.js'; +export * from './external_calls.js'; +export * from './environment_getters.js'; \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index bfb4150caf3..37ca85998a0 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -2,6 +2,7 @@ import { Add, And, CMov, + Call, CalldataCopy, Cast, Div, @@ -23,6 +24,7 @@ import { Set, Shl, Shr, + StaticCall, Sub, Xor, } from '../opcodes/index.js'; @@ -102,8 +104,8 @@ const INSTRUCTION_SET: InstructionSet = new Map Date: Wed, 31 Jan 2024 16:04:05 +0000 Subject: [PATCH 11/25] environment getters: serialization tests missing --- .../avm/opcodes/environment_getters.test.ts | 24 +- .../src/avm/opcodes/environment_getters.ts | 279 ++++++++++-------- .../serialization/bytecode_serialization.ts | 50 ++-- 3 files changed, 192 insertions(+), 161 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 4dd6b0abb85..965fa753d5b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -39,42 +39,42 @@ describe('Environment getters instructions', () => { it('Should read address correctly', async () => { const address = new Fr(123456n); - await envGetterTest('address', address, new Address(0)); + await envGetterTest('address', address, new Address(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read storage address correctly', async () => { const address = new Fr(123456n); - await envGetterTest('storageAddress', address, new StorageAddress(0)); + await envGetterTest('storageAddress', address, new StorageAddress(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read Portal correctly', async () => { const portal = new Fr(123456n); - await envGetterTest('portal', portal, new Portal(0)); + await envGetterTest('portal', portal, new Portal(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read FeePerL1Gas correctly', async () => { const feePerL1Gas = new Fr(123456n); - await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(0)); + await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read FeePerL2Gas correctly', async () => { const feePerL2Gas = new Fr(123456n); - await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(0)); + await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read FeePerDAGas correctly', async () => { const feePerDaGas = new Fr(123456n); - await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(0)); + await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read Origin correctly', async () => { const origin = new Fr(123456n); - await envGetterTest('origin', origin, new Origin(0)); + await envGetterTest('origin', origin, new Origin(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read Sender correctly', async () => { const sender = new Fr(123456n); - await envGetterTest('sender', sender, new Sender(0)); + await envGetterTest('sender', sender, new Sender(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); describe('Global Variables', () => { @@ -90,22 +90,22 @@ describe('Environment getters instructions', () => { it('Should read chainId', async () => { const chainId = new Fr(123456n); - await readGlobalVariableTest('chainId', chainId, new ChainId(0)); + await readGlobalVariableTest('chainId', chainId, new ChainId(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read version', async () => { const version = new Fr(123456n); - await readGlobalVariableTest('version', version, new Version(0)); + await readGlobalVariableTest('version', version, new Version(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read block number', async () => { const blockNumber = new Fr(123456n); - await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(0)); + await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); it('Should read timestamp', async () => { const timestamp = new Fr(123456n); - await readGlobalVariableTest('timestamp', timestamp, new Timestamp(0)); + await readGlobalVariableTest('timestamp', timestamp, new Timestamp(/*indirect=*/ 0, /*dstOffset=*/ 0)); }); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index 18e97575b96..29be9a96f42 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -1,211 +1,230 @@ +import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; -export class Address extends Instruction { - static type: string = 'ADDRESS'; - static numberOfOperands = 1; +abstract class GetterInstruction extends Instruction { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: GetterInstruction) => c.opcode, OperandType.UINT8], + [(c: GetterInstruction) => c.indirect, OperandType.UINT8], + [(c: GetterInstruction) => c.dstOffset, OperandType.UINT32], + ]; - constructor(private destOffset: number) { + constructor(protected indirect: number, protected dstOffset: number) { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { address } = machineState.executionEnvironment; + protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { + const res = deserialize(buf, GetterInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; + } - machineState.memory.set(this.destOffset, new Field(address)); + public serialize(): Buffer { + return serialize(GetterInstruction.wireFormat, this); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const res = new Field(this.getIt(machineState.executionEnvironment)); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } + + protected abstract get opcode(): Opcode; + protected abstract getIt(env: AvmExecutionEnvironment): any; } -export class StorageAddress extends Instruction { - static type: string = 'STORAGEADDRESS'; - static numberOfOperands = 1; +export class Address extends GetterInstruction { + static type: string = 'ADDRESS'; + static readonly opcode: Opcode = Opcode.ADDRESS; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Address.opcode; + } + protected getIt(env: AvmExecutionEnvironment): any { + return env.address; } + public static deserialize(buf: BufferCursor | Buffer): Address { + return new Address(...GetterInstruction.deserializeBase(buf)); + } +} - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { storageAddress } = machineState.executionEnvironment; +export class StorageAddress extends GetterInstruction { + static type: string = 'STORAGEADDRESS'; + static readonly opcode: Opcode = Opcode.STORAGEADDRESS; - machineState.memory.set(this.destOffset, new Field(storageAddress)); - this.incrementPc(machineState); + protected get opcode() { + return StorageAddress.opcode; + } + protected getIt(env: AvmExecutionEnvironment): any { + return env.storageAddress; + } + public static deserialize(buf: BufferCursor | Buffer): StorageAddress { + return new StorageAddress(...GetterInstruction.deserializeBase(buf)); } } -export class Sender extends Instruction { +export class Sender extends GetterInstruction { static type: string = 'SENDER'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.SENDER; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Sender.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { sender } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(sender)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.sender; + } + public static deserialize(buf: BufferCursor | Buffer): Sender { + return new Sender(...GetterInstruction.deserializeBase(buf)); } } -export class Origin extends Instruction { +export class Origin extends GetterInstruction { static type: string = 'ORIGIN'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.ORIGIN; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Origin.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { origin } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(origin)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.origin; + } + public static deserialize(buf: BufferCursor | Buffer): Origin { + return new Origin(...GetterInstruction.deserializeBase(buf)); } } -export class FeePerL1Gas extends Instruction { +export class FeePerL1Gas extends GetterInstruction { static type: string = 'FEEPERL1GAS'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.FEEPERL1GAS; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return FeePerL1Gas.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { feePerL1Gas } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(feePerL1Gas)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.feePerL1Gas; + } + public static deserialize(buf: BufferCursor | Buffer): FeePerL1Gas { + return new FeePerL1Gas(...GetterInstruction.deserializeBase(buf)); } } -export class FeePerL2Gas extends Instruction { +export class FeePerL2Gas extends GetterInstruction { static type: string = 'FEEPERL2GAS'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.FEEPERL2GAS; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return FeePerL2Gas.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { feePerL2Gas } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(feePerL2Gas)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.feePerL2Gas; + } + public static deserialize(buf: BufferCursor | Buffer): FeePerL2Gas { + return new FeePerL2Gas(...GetterInstruction.deserializeBase(buf)); } } -export class FeePerDAGas extends Instruction { +export class FeePerDAGas extends GetterInstruction { static type: string = 'FEEPERDAGAS'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.FEEPERDAGAS; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return FeePerDAGas.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { feePerDaGas } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(feePerDaGas)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.feePerDaGas; + } + public static deserialize(buf: BufferCursor | Buffer): FeePerDAGas { + return new FeePerDAGas(...GetterInstruction.deserializeBase(buf)); } } -export class Portal extends Instruction { +export class Portal extends GetterInstruction { static type: string = 'PORTAL'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.PORTAL; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Portal.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { portal } = machineState.executionEnvironment; - - machineState.memory.set(this.destOffset, new Field(portal.toField())); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.portal.toField(); + } + public static deserialize(buf: BufferCursor | Buffer): Portal { + return new Portal(...GetterInstruction.deserializeBase(buf)); } } -export class ChainId extends Instruction { +export class ChainId extends GetterInstruction { static type: string = 'CHAINID'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.CHAINID; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return ChainId.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { chainId } = machineState.executionEnvironment.globals; - - machineState.memory.set(this.destOffset, new Field(chainId)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.globals.chainId; + } + public static deserialize(buf: BufferCursor | Buffer): ChainId { + return new ChainId(...GetterInstruction.deserializeBase(buf)); } } -export class Version extends Instruction { +export class Version extends GetterInstruction { static type: string = 'VERSION'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.VERSION; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Version.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { version } = machineState.executionEnvironment.globals; - - machineState.memory.set(this.destOffset, new Field(version)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.globals.version; + } + public static deserialize(buf: BufferCursor | Buffer): Version { + return new Version(...GetterInstruction.deserializeBase(buf)); } } -export class BlockNumber extends Instruction { +export class BlockNumber extends GetterInstruction { static type: string = 'BLOCKNUMBER'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.BLOCKNUMBER; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return BlockNumber.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { blockNumber } = machineState.executionEnvironment.globals; - - machineState.memory.set(this.destOffset, new Field(blockNumber)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.globals.blockNumber; + } + public static deserialize(buf: BufferCursor | Buffer): BlockNumber { + return new BlockNumber(...GetterInstruction.deserializeBase(buf)); } } -export class Timestamp extends Instruction { +export class Timestamp extends GetterInstruction { static type: string = 'TIMESTAMP'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.TIMESTAMP; - constructor(private destOffset: number) { - super(); + protected get opcode() { + return Timestamp.opcode; } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { - const { timestamp } = machineState.executionEnvironment.globals; - - machineState.memory.set(this.destOffset, new Field(timestamp)); - - this.incrementPc(machineState); + protected getIt(env: AvmExecutionEnvironment): any { + return env.globals.timestamp; + } + public static deserialize(buf: BufferCursor | Buffer): Timestamp { + return new Timestamp(...GetterInstruction.deserializeBase(buf)); } } -// export class Coinbase extends Instruction { +// export class Coinbase extends GetterInstruction { // static type: string = 'COINBASE'; // static numberOfOperands = 1; @@ -223,7 +242,7 @@ export class Timestamp extends Instruction { // } // // TODO: are these even needed within the block? (both block gas limit variables - why does the execution env care?) -// export class BlockL1GasLimit extends Instruction { +// export class BlockL1GasLimit extends GetterInstruction { // static type: string = 'BLOCKL1GASLIMIT'; // static numberOfOperands = 1; @@ -240,7 +259,7 @@ export class Timestamp extends Instruction { // } // } -// export class BlockL2GasLimit extends Instruction { +// export class BlockL2GasLimit extends GetterInstruction { // static type: string = 'BLOCKL2GASLIMIT'; // static numberOfOperands = 1; @@ -257,7 +276,7 @@ export class Timestamp extends Instruction { // } // } -// export class BlockDAGasLimit extends Instruction { +// export class BlockDAGasLimit extends GetterInstruction { // static type: string = 'BLOCKDAGASLIMIT'; // static numberOfOperands = 1; diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index 37ca85998a0..da71e6af2ba 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -1,12 +1,18 @@ import { Add, + Address, And, + BlockNumber, CMov, Call, CalldataCopy, Cast, + ChainId, Div, Eq, + FeePerDAGas, + FeePerL1Gas, + FeePerL2Gas, InternalCall, InternalReturn, Jump, @@ -17,16 +23,23 @@ import { Mul, Not, Or, + Origin, + Portal, Return, Revert, SLoad, SStore, + Sender, Set, Shl, Shr, StaticCall, + StorageAddress, Sub, + Timestamp, + Version, Xor, + Timestamp, } from '../opcodes/index.js'; import { Instruction } from '../opcodes/instruction.js'; import { BufferCursor } from './buffer_cursor.js'; @@ -55,25 +68,24 @@ const INSTRUCTION_SET: InstructionSet = new Map Date: Wed, 31 Jan 2024 16:24:52 +0000 Subject: [PATCH 12/25] accrued substate: serialization tests missing --- .../src/avm/opcodes/accrued_substate.test.ts | 18 ++-- .../src/avm/opcodes/accrued_substate.ts | 94 +++++++++++++++++-- .../acir-simulator/src/avm/opcodes/index.ts | 6 +- .../serialization/bytecode_serialization.ts | 21 +++-- 4 files changed, 111 insertions(+), 28 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index fd24b23eda4..49c8c58e001 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -22,7 +22,7 @@ describe('Accrued Substate', () => { const value = new Field(69n); machineState.memory.set(0, value); - await new EmitNoteHash(0).execute(machineState, journal); + await new EmitNoteHash(/*indirect=*/ 0, 0).execute(machineState, journal); const journalState = journal.flush(); const expected = [value.toFr()]; @@ -33,7 +33,7 @@ describe('Accrued Substate', () => { const value = new Field(69n); machineState.memory.set(0, value); - await new EmitNullifier(0).execute(machineState, journal); + await new EmitNullifier(/*indirect=*/ 0, 0).execute(machineState, journal); const journalState = journal.flush(); const expected = [value.toFr()]; @@ -48,7 +48,7 @@ describe('Accrued Substate', () => { const length = values.length; - await new EmitUnencryptedLog(startOffset, length).execute(machineState, journal); + await new EmitUnencryptedLog(/*indirect=*/ 0, startOffset, length).execute(machineState, journal); const journalState = journal.flush(); const expected = values.map(v => v.toFr()); @@ -63,7 +63,7 @@ describe('Accrued Substate', () => { const length = values.length; - await new SendL2ToL1Message(startOffset, length).execute(machineState, journal); + await new SendL2ToL1Message(/*indirect=*/ 0, startOffset, length).execute(machineState, journal); const journalState = journal.flush(); const expected = values.map(v => v.toFr()); @@ -75,15 +75,15 @@ describe('Accrued Substate', () => { machineState = new AvmMachineState(executionEnvironment); const instructions = [ - new EmitNoteHash(0), - new EmitNullifier(0), - new EmitUnencryptedLog(0, 1), - new SendL2ToL1Message(0, 1), + new EmitNoteHash(/*indirect=*/ 0, 0), + new EmitNullifier(/*indirect=*/ 0, 0), + new EmitUnencryptedLog(/*indirect=*/ 0, 0, 1), + new SendL2ToL1Message(/*indirect=*/ 0, 0, 1), ]; for (const instruction of instructions) { const inst = () => instruction.execute(machineState, journal); - await expect(inst()).rejects.toThrowError(StaticCallStorageAlterError); + await expect(inst()).rejects.toThrow(StaticCallStorageAlterError); } }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index de54edaa0c7..5a6ce67f599 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -1,16 +1,41 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/journal.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { StaticCallStorageAlterError } from './storage.js'; export class EmitNoteHash extends Instruction { static type: string = 'EMITNOTEHASH'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.EMITNOTEHASH; - constructor(private noteHashOffset: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: EmitNoteHash) => EmitNoteHash.opcode, OperandType.UINT8], + [(c: EmitNoteHash) => c.indirect, OperandType.UINT8], + [(c: EmitNoteHash) => c.noteHashOffset, OperandType.UINT32], + ]; + + constructor(private indirect: number, private noteHashOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): EmitNoteHash { + const res = deserialize(buf, EmitNoteHash.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new EmitNoteHash(...args); + } + + public serialize(): Buffer { + return serialize(EmitNoteHash.wireFormat, this); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -25,12 +50,29 @@ export class EmitNoteHash extends Instruction { export class EmitNullifier extends Instruction { static type: string = 'EMITNULLIFIER'; - static numberOfOperands = 1; + static readonly opcode: Opcode = Opcode.EMITNULLIFIER; - constructor(private nullifierOffset: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: EmitNullifier) => EmitNullifier.opcode, OperandType.UINT8], + [(c: EmitNullifier) => c.indirect, OperandType.UINT8], + [(c: EmitNullifier) => c.nullifierOffset, OperandType.UINT32], + ]; + + constructor(private indirect: number, private nullifierOffset: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): EmitNullifier { + const res = deserialize(buf, EmitNullifier.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new EmitNullifier(...args); + } + + public serialize(): Buffer { + return serialize(EmitNullifier.wireFormat, this); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -45,12 +87,30 @@ export class EmitNullifier extends Instruction { export class EmitUnencryptedLog extends Instruction { static type: string = 'EMITUNENCRYPTEDLOG'; - static numberOfOperands = 2; + static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG; - constructor(private logOffset: number, private logSize: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: EmitUnencryptedLog) => EmitUnencryptedLog.opcode, OperandType.UINT8], + [(c: EmitUnencryptedLog) => c.indirect, OperandType.UINT8], + [(c: EmitUnencryptedLog) => c.logOffset, OperandType.UINT32], + [(c: EmitUnencryptedLog) => c.logSize, OperandType.UINT32], + ]; + + constructor(private indirect: number, private logOffset: number, private logSize: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): EmitUnencryptedLog { + const res = deserialize(buf, EmitUnencryptedLog.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new EmitUnencryptedLog(...args); + } + + public serialize(): Buffer { + return serialize(EmitUnencryptedLog.wireFormat, this); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -65,12 +125,30 @@ export class EmitUnencryptedLog extends Instruction { export class SendL2ToL1Message extends Instruction { static type: string = 'EMITUNENCRYPTEDLOG'; - static numberOfOperands = 2; + static readonly opcode: Opcode = Opcode.SENDL2TOL1MSG; - constructor(private msgOffset: number, private msgSize: number) { + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(_: SendL2ToL1Message) => SendL2ToL1Message.opcode, OperandType.UINT8], + [(c: SendL2ToL1Message) => c.indirect, OperandType.UINT8], + [(c: SendL2ToL1Message) => c.msgOffset, OperandType.UINT32], + [(c: SendL2ToL1Message) => c.msgSize, OperandType.UINT32], + ]; + + constructor(private indirect: number, private msgOffset: number, private msgSize: number) { super(); } + public static deserialize(buf: BufferCursor | Buffer): SendL2ToL1Message { + const res = deserialize(buf, SendL2ToL1Message.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return new SendL2ToL1Message(...args); + } + + public serialize(): Buffer { + return serialize(SendL2ToL1Message.wireFormat, this); + } + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index 8f84f04e30c..cfa33a5406b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -5,5 +5,7 @@ export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; export * from './storage.js'; -export * from './external_calls.js'; -export * from './environment_getters.js'; \ No newline at end of file +// FIXME: dependency cycle +// export * from './external_calls.js'; +export * from './environment_getters.js'; +export * from './accrued_substate.js'; \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index da71e6af2ba..d19e0085984 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -4,7 +4,7 @@ import { And, BlockNumber, CMov, - Call, + // Call, CalldataCopy, Cast, ChainId, @@ -33,13 +33,16 @@ import { Set, Shl, Shr, - StaticCall, + // StaticCall, StorageAddress, Sub, Timestamp, Version, Xor, - Timestamp, + EmitUnencryptedLog, + SendL2ToL1Message, + EmitNoteHash, + EmitNullifier, } from '../opcodes/index.js'; import { Instruction } from '../opcodes/instruction.js'; import { BufferCursor } from './buffer_cursor.js'; @@ -108,16 +111,16 @@ const INSTRUCTION_SET: InstructionSet = new Map Date: Wed, 31 Jan 2024 16:36:36 +0000 Subject: [PATCH 13/25] fix tests and formatting --- .../acir-simulator/src/avm/avm_context.ts | 4 +- .../acir-simulator/src/avm/index.test.ts | 21 ++- .../src/avm/interpreter/interpreter.test.ts | 7 +- .../src/avm/opcodes/arithmetic.test.ts | 120 ++++++++++++--- .../acir-simulator/src/avm/opcodes/bitwise.ts | 2 +- .../src/avm/opcodes/comparators.ts | 1 - .../acir-simulator/src/avm/opcodes/index.ts | 2 +- .../src/avm/opcodes/instruction_impl.ts | 138 +++++++++--------- .../src/avm/serialization/buffer_cursor.ts | 2 +- .../serialization/bytecode_serialization.ts | 14 +- .../instruction_serialization.test.ts | 4 +- 11 files changed, 193 insertions(+), 122 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index f92d040ff6e..9816fda32d8 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -7,7 +7,9 @@ import { AvmMessageCallResult } from './avm_message_call_result.js'; import { AvmInterpreterError, executeAvm } from './interpreter/index.js'; import { AvmJournal } from './journal/journal.js'; import { Instruction } from './opcodes/instruction.js'; -import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; // FIXME: dependency cycle. +import { decodeFromBytecode } from './serialization/bytecode_serialization.js'; + +// FIXME: dependency cycle. /** * Avm Executor manages the execution of the AVM diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 8fb3a49dab0..310e8a5634b 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -3,12 +3,12 @@ import { Fr } from '@aztec/foundation/fields'; import { mock } from 'jest-mock-extended'; import { AvmMachineState } from './avm_machine_state.js'; +import { TypeTag } from './avm_memory_types.js'; import { initExecutionEnvironment } from './fixtures/index.js'; import { executeAvm } from './interpreter/interpreter.js'; import { AvmJournal } from './journal/journal.js'; -import { decodeBytecode } from './opcodes/decode_bytecode.js'; -import { encodeToBytecode } from './opcodes/encode_to_bytecode.js'; -import { Opcode } from './opcodes/opcodes.js'; +import { Add, CalldataCopy, Return } from './opcodes/index.js'; +import { decodeFromBytecode, encodeToBytecode } from './serialization/bytecode_serialization.js'; describe('avm', () => { it('Should execute bytecode', async () => { @@ -16,17 +16,14 @@ describe('avm', () => { const journal = mock(); // Construct bytecode - const calldataCopyArgs = [0, 2, 0]; - const addArgs = [0, 1, 2]; - const returnArgs = [2, 1]; - - const calldataCopyBytecode = encodeToBytecode(Opcode.CALLDATACOPY, calldataCopyArgs); - const addBytecode = encodeToBytecode(Opcode.ADD, addArgs); - const returnBytecode = encodeToBytecode(Opcode.RETURN, returnArgs); - const fullBytecode = Buffer.concat([calldataCopyBytecode, addBytecode, returnBytecode]); + const bytecode = encodeToBytecode([ + new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), + new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), + ]); // Decode bytecode into instructions - const instructions = decodeBytecode(fullBytecode); + const instructions = decodeFromBytecode(bytecode); // Execute instructions const context = new AvmMachineState(initExecutionEnvironment({ calldata })); 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 e5055cdf903..c0111f067ac 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts @@ -3,6 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; +import { TypeTag } from '../avm_memory_types.js'; import { initExecutionEnvironment } from '../fixtures/index.js'; import { AvmJournal } from '../journal/journal.js'; import { Add } from '../opcodes/arithmetic.js'; @@ -22,9 +23,9 @@ describe('interpreter', () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; const instructions: Instruction[] = [ - new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), - new Add(/*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), - new Return(/*returnOffset=*/ 2, /*copySize=*/ 1), + new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), + new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), ]; const machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 1f8164cefec..0c67f4f980d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -25,11 +25,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); const inst = Add.deserialize(buf); @@ -61,11 +70,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); expect(inst.serialize()).toEqual(expected); }); @@ -121,11 +139,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); const inst = Sub.deserialize(buf); @@ -157,11 +184,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); expect(inst.serialize()).toEqual(expected); }); @@ -197,11 +233,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); const inst = Mul.deserialize(buf); @@ -233,11 +278,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); expect(inst.serialize()).toEqual(expected); }); @@ -293,11 +347,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); const inst = Div.deserialize(buf); @@ -329,11 +392,20 @@ describe('Arithmetic Instructions', () => { // inTag TypeTag.FIELD, // aOffset - 0x12, 0x34, 0x56, 0x78, + 0x12, + 0x34, + 0x56, + 0x78, // bOffset - 0x23, 0x45, 0x67, 0x89, + 0x23, + 0x45, + 0x67, + 0x89, // dstOffset - 0x34, 0x56, 0x78, 0x9a, + 0x34, + 0x56, + 0x78, + 0x9a, ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 125daa46636..13bb6d9a909 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,5 +1,5 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { IntegralValue, TypeTag } from '../avm_memory_types.js'; +import { IntegralValue } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode } from '../serialization/instruction_serialization.js'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index fd472256fcf..89bd92e369a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -53,7 +53,6 @@ export class Lt extends ThreeOperandInstruction { return new Lt(...args); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index cfa33a5406b..56874ca5073 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -8,4 +8,4 @@ export * from './storage.js'; // FIXME: dependency cycle // export * from './external_calls.js'; export * from './environment_getters.js'; -export * from './accrued_substate.js'; \ No newline at end of file +export * from './accrued_substate.js'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts index 16b8785d0da..98e348100c9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -1,73 +1,75 @@ -import { BufferCursor } from "../serialization/buffer_cursor.js"; -import { Opcode, OperandPair, OperandType, deserialize, serialize } from "../serialization/instruction_serialization.js"; -import { Instruction } from "./instruction.js"; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { + Opcode, + OperandPair, + OperandType, + deserialize, + serialize, +} from '../serialization/instruction_serialization.js'; +import { Instruction } from './instruction.js'; export abstract class TwoOperandInstruction extends Instruction { - // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ - [(c: TwoOperandInstruction) => c.opcode, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.indirect, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.inTag, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.aOffset, OperandType.UINT32], - [(c: TwoOperandInstruction) => c.dstOffset, OperandType.UINT32], - ]; - - constructor( - protected indirect: number, - protected inTag: number, - protected aOffset: number, - protected dstOffset: number, - ) { - super(); - } - - protected static deserializeBase( - buf: BufferCursor | Buffer, - ): ConstructorParameters { - const res = deserialize(buf, TwoOperandInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(TwoOperandInstruction.wireFormat, this); - } - - protected abstract get opcode(): Opcode; + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: TwoOperandInstruction) => c.opcode, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.indirect, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.inTag, OperandType.UINT8], + [(c: TwoOperandInstruction) => c.aOffset, OperandType.UINT32], + [(c: TwoOperandInstruction) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + protected indirect: number, + protected inTag: number, + protected aOffset: number, + protected dstOffset: number, + ) { + super(); + } + + protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { + const res = deserialize(buf, TwoOperandInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; } + public serialize(): Buffer { + return serialize(TwoOperandInstruction.wireFormat, this); + } + + protected abstract get opcode(): Opcode; +} + export abstract class ThreeOperandInstruction extends Instruction { - // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ - [(c: ThreeOperandInstruction) => c.opcode, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.indirect, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.inTag, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.aOffset, OperandType.UINT32], - [(c: ThreeOperandInstruction) => c.bOffset, OperandType.UINT32], - [(c: ThreeOperandInstruction) => c.dstOffset, OperandType.UINT32], - ]; - - constructor( - protected indirect: number, - protected inTag: number, - protected aOffset: number, - protected bOffset: number, - protected dstOffset: number, - ) { - super(); - } - - protected static deserializeBase( - buf: BufferCursor | Buffer, - ): ConstructorParameters { - const res = deserialize(buf, ThreeOperandInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(ThreeOperandInstruction.wireFormat, this); - } - - protected abstract get opcode(): Opcode; - } \ No newline at end of file + // Instruction wire format with opcode. + private static readonly wireFormat: OperandPair[] = [ + [(c: ThreeOperandInstruction) => c.opcode, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.indirect, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.inTag, OperandType.UINT8], + [(c: ThreeOperandInstruction) => c.aOffset, OperandType.UINT32], + [(c: ThreeOperandInstruction) => c.bOffset, OperandType.UINT32], + [(c: ThreeOperandInstruction) => c.dstOffset, OperandType.UINT32], + ]; + + constructor( + protected indirect: number, + protected inTag: number, + protected aOffset: number, + protected bOffset: number, + protected dstOffset: number, + ) { + super(); + } + + protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { + const res = deserialize(buf, ThreeOperandInstruction.wireFormat); + const params = res.slice(1); // Remove opcode. + return params as ConstructorParameters; + } + + public serialize(): Buffer { + return serialize(ThreeOperandInstruction.wireFormat, this); + } + + protected abstract get opcode(): Opcode; +} diff --git a/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts index aab7bf59e45..237d9ae24f1 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/buffer_cursor.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'assert'; /* * A Buffer-like class that automatically advances the position. -*/ + */ export class BufferCursor { constructor(private _buffer: Buffer, private _position: number = 0) {} diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index d19e0085984..ec7092766fd 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -3,12 +3,14 @@ import { Address, And, BlockNumber, - CMov, - // Call, + CMov, // Call, CalldataCopy, Cast, ChainId, Div, + EmitNoteHash, + EmitNullifier, + EmitUnencryptedLog, Eq, FeePerDAGas, FeePerL1Gas, @@ -29,20 +31,16 @@ import { Revert, SLoad, SStore, + SendL2ToL1Message, Sender, Set, Shl, - Shr, - // StaticCall, + Shr, // StaticCall, StorageAddress, Sub, Timestamp, Version, Xor, - EmitUnencryptedLog, - SendL2ToL1Message, - EmitNoteHash, - EmitNullifier, } from '../opcodes/index.js'; import { Instruction } from '../opcodes/instruction.js'; import { BufferCursor } from './buffer_cursor.js'; diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts index 6bcac9df66b..6bb0d4e30d6 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts @@ -1,6 +1,5 @@ import { BufferCursor } from './buffer_cursor.js'; import { OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; -import { encodeToBytecode } from './bytecode_serialization.js'; class InstA { constructor(private a: number, private b: number, private c: number, private d: bigint, private e: bigint) {} @@ -61,7 +60,8 @@ describe('Instruction Serialization', () => { 'hex', ); - const params = deserialize(new BufferCursor(buffer), InstA.wireFormat) as ConstructorParameters; + const deserializedParams = deserialize(new BufferCursor(buffer), InstA.wireFormat); + const params = deserializedParams.slice(1) as ConstructorParameters; // Drop opcode. const actual = new InstA(...params); const expected = new InstA(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); From f7fb3e3366e6980afa9bfdfa000eedf67a486210 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:48:41 +0000 Subject: [PATCH 14/25] feat: hoist deserialisation --- .../src/avm/opcodes/accrued_substate.ts | 36 +++------------ .../src/avm/opcodes/arithmetic.ts | 17 ------- .../acir-simulator/src/avm/opcodes/bitwise.ts | 24 ---------- .../src/avm/opcodes/comparators.ts | 14 ------ .../src/avm/opcodes/control_flow.ts | 45 +++---------------- .../src/avm/opcodes/environment_getters.ts | 38 +--------------- .../src/avm/opcodes/external_calls.ts | 16 +------ .../src/avm/opcodes/instruction.ts | 8 ++++ .../src/avm/opcodes/instruction_impl.ts | 4 +- .../acir-simulator/src/avm/opcodes/memory.ts | 34 ++------------ .../acir-simulator/src/avm/opcodes/storage.ts | 10 +---- .../src/avm/opcodes/tinker.test.ts | 25 +++++++++++ .../instruction_serialization.ts | 1 + 13 files changed, 56 insertions(+), 216 deletions(-) create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index 5a6ce67f599..3453a8f2b93 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -1,14 +1,13 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/journal.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, serialize, } from '../serialization/instruction_serialization.js'; -import { Instruction } from './instruction.js'; +import { Instruction, +} from './instruction.js'; import { StaticCallStorageAlterError } from './storage.js'; export class EmitNoteHash extends Instruction { @@ -16,7 +15,7 @@ export class EmitNoteHash extends Instruction { static readonly opcode: Opcode = Opcode.EMITNOTEHASH; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: EmitNoteHash) => EmitNoteHash.opcode, OperandType.UINT8], [(c: EmitNoteHash) => c.indirect, OperandType.UINT8], [(c: EmitNoteHash) => c.noteHashOffset, OperandType.UINT32], @@ -26,12 +25,6 @@ export class EmitNoteHash extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): EmitNoteHash { - const res = deserialize(buf, EmitNoteHash.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new EmitNoteHash(...args); - } - public serialize(): Buffer { return serialize(EmitNoteHash.wireFormat, this); } @@ -53,7 +46,7 @@ export class EmitNullifier extends Instruction { static readonly opcode: Opcode = Opcode.EMITNULLIFIER; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: EmitNullifier) => EmitNullifier.opcode, OperandType.UINT8], [(c: EmitNullifier) => c.indirect, OperandType.UINT8], [(c: EmitNullifier) => c.nullifierOffset, OperandType.UINT32], @@ -63,12 +56,6 @@ export class EmitNullifier extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): EmitNullifier { - const res = deserialize(buf, EmitNullifier.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new EmitNullifier(...args); - } - public serialize(): Buffer { return serialize(EmitNullifier.wireFormat, this); } @@ -90,7 +77,7 @@ export class EmitUnencryptedLog extends Instruction { static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: EmitUnencryptedLog) => EmitUnencryptedLog.opcode, OperandType.UINT8], [(c: EmitUnencryptedLog) => c.indirect, OperandType.UINT8], [(c: EmitUnencryptedLog) => c.logOffset, OperandType.UINT32], @@ -101,12 +88,6 @@ export class EmitUnencryptedLog extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): EmitUnencryptedLog { - const res = deserialize(buf, EmitUnencryptedLog.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new EmitUnencryptedLog(...args); - } - public serialize(): Buffer { return serialize(EmitUnencryptedLog.wireFormat, this); } @@ -128,7 +109,7 @@ export class SendL2ToL1Message extends Instruction { static readonly opcode: Opcode = Opcode.SENDL2TOL1MSG; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: SendL2ToL1Message) => SendL2ToL1Message.opcode, OperandType.UINT8], [(c: SendL2ToL1Message) => c.indirect, OperandType.UINT8], [(c: SendL2ToL1Message) => c.msgOffset, OperandType.UINT32], @@ -139,11 +120,6 @@ export class SendL2ToL1Message extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): SendL2ToL1Message { - const res = deserialize(buf, SendL2ToL1Message.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new SendL2ToL1Message(...args); - } public serialize(): Buffer { return serialize(SendL2ToL1Message.wireFormat, this); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index de0c353ca15..b2a8b7d5cca 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,6 +1,5 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/index.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; @@ -16,10 +15,6 @@ export class Add extends ThreeOperandInstruction { return Add.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Add { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Add(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -44,10 +39,6 @@ export class Sub extends ThreeOperandInstruction { return Sub.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Sub { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Sub(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -72,10 +63,6 @@ export class Mul extends ThreeOperandInstruction { return Mul.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Mul { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Mul(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -100,10 +87,6 @@ export class Div extends ThreeOperandInstruction { return Div.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Div { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Div(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 13bb6d9a909..3cf18c3f891 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -18,10 +18,6 @@ export class And extends ThreeOperandInstruction { return And.opcode; } - public static deserialize(buf: BufferCursor | Buffer): And { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new And(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -48,10 +44,6 @@ export class Or extends ThreeOperandInstruction { return Or.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Or { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Or(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -78,10 +70,6 @@ export class Xor extends ThreeOperandInstruction { return Xor.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Xor { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Xor(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -108,10 +96,6 @@ export class Not extends TwoOperandInstruction { return Not.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Not { - const args = TwoOperandInstruction.deserializeBase(buf); - return new Not(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset); @@ -137,10 +121,6 @@ export class Shl extends ThreeOperandInstruction { return Shl.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Shl { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Shl(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -167,10 +147,6 @@ export class Shr extends ThreeOperandInstruction { return Shr.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Shr { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Shr(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 89bd92e369a..555faaabdd6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,6 +1,5 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/index.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; @@ -17,10 +16,6 @@ export class Eq extends ThreeOperandInstruction { return Eq.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Eq { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Eq(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -48,11 +43,6 @@ export class Lt extends ThreeOperandInstruction { return Lt.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Lt { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Lt(...args); - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -79,10 +69,6 @@ export class Lte extends ThreeOperandInstruction { return Lte.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Lte { - const args = ThreeOperandInstruction.deserializeBase(buf); - return new Lte(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); 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 bf46f56dbba..c67737e9c83 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,12 +1,10 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { IntegralValue } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, serialize, } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; @@ -16,7 +14,7 @@ export class Return extends Instruction { static readonly opcode: Opcode = Opcode.RETURN; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: Return) => Return.opcode, OperandType.UINT8], [(c: Return) => c.indirect, OperandType.UINT8], [(c: Return) => c.returnOffset, OperandType.UINT32], @@ -27,12 +25,6 @@ export class Return extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Return { - const res = deserialize(buf, Return.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Return(...args); - } - public serialize(): Buffer { return serialize(Return.wireFormat, this); } @@ -51,7 +43,7 @@ export class Revert extends Instruction { static readonly opcode: Opcode = Opcode.REVERT; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: Revert) => Revert.opcode, OperandType.UINT8], [(c: Revert) => c.indirect, OperandType.UINT8], [(c: Revert) => c.returnOffset, OperandType.UINT32], @@ -62,11 +54,6 @@ export class Revert extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Revert { - const res = deserialize(buf, Revert.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Revert(...args); - } public serialize(): Buffer { return serialize(Revert.wireFormat, this); @@ -87,7 +74,7 @@ export class Jump extends Instruction { static readonly opcode: Opcode = Opcode.JUMP; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: Jump) => Jump.opcode, OperandType.UINT8], [(c: Jump) => c.jumpOffset, OperandType.UINT32], ]; @@ -96,11 +83,6 @@ export class Jump extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Jump { - const res = deserialize(buf, Jump.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Jump(...args); - } public serialize(): Buffer { return serialize(Jump.wireFormat, this); @@ -116,7 +98,7 @@ export class JumpI extends Instruction { static readonly opcode: Opcode = Opcode.JUMPI; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: JumpI) => JumpI.opcode, OperandType.UINT8], [(c: JumpI) => c.indirect, OperandType.UINT8], [(c: JumpI) => c.loc, OperandType.UINT32], @@ -127,11 +109,6 @@ export class JumpI extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): JumpI { - const res = deserialize(buf, JumpI.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new JumpI(...args); - } public serialize(): Buffer { return serialize(JumpI.wireFormat, this); @@ -154,7 +131,7 @@ export class InternalCall extends Instruction { static readonly opcode: Opcode = Opcode.INTERNALCALL; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: InternalCall) => InternalCall.opcode, OperandType.UINT8], [(c: InternalCall) => c.loc, OperandType.UINT32], ]; @@ -163,11 +140,6 @@ export class InternalCall extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): InternalCall { - const res = deserialize(buf, InternalCall.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new InternalCall(...args); - } public serialize(): Buffer { return serialize(InternalCall.wireFormat, this); @@ -184,7 +156,7 @@ export class InternalReturn extends Instruction { static readonly opcode: Opcode = Opcode.INTERNALRETURN; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: InternalReturn) => InternalReturn.opcode, OperandType.UINT8], ]; @@ -192,11 +164,6 @@ export class InternalReturn extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): InternalReturn { - deserialize(buf, InternalReturn.wireFormat); // only contains opcode. - return new InternalReturn(); - } - public serialize(): Buffer { return serialize(InternalReturn.wireFormat, this); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index 29be9a96f42..ed04370028b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -14,7 +14,7 @@ import { Instruction } from './instruction.js'; abstract class GetterInstruction extends Instruction { // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(c: GetterInstruction) => c.opcode, OperandType.UINT8], [(c: GetterInstruction) => c.indirect, OperandType.UINT8], [(c: GetterInstruction) => c.dstOffset, OperandType.UINT32], @@ -54,9 +54,6 @@ export class Address extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.address; } - public static deserialize(buf: BufferCursor | Buffer): Address { - return new Address(...GetterInstruction.deserializeBase(buf)); - } } export class StorageAddress extends GetterInstruction { @@ -69,9 +66,6 @@ export class StorageAddress extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.storageAddress; } - public static deserialize(buf: BufferCursor | Buffer): StorageAddress { - return new StorageAddress(...GetterInstruction.deserializeBase(buf)); - } } export class Sender extends GetterInstruction { @@ -84,9 +78,6 @@ export class Sender extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.sender; } - public static deserialize(buf: BufferCursor | Buffer): Sender { - return new Sender(...GetterInstruction.deserializeBase(buf)); - } } export class Origin extends GetterInstruction { @@ -99,9 +90,6 @@ export class Origin extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.origin; } - public static deserialize(buf: BufferCursor | Buffer): Origin { - return new Origin(...GetterInstruction.deserializeBase(buf)); - } } export class FeePerL1Gas extends GetterInstruction { @@ -114,9 +102,6 @@ export class FeePerL1Gas extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.feePerL1Gas; } - public static deserialize(buf: BufferCursor | Buffer): FeePerL1Gas { - return new FeePerL1Gas(...GetterInstruction.deserializeBase(buf)); - } } export class FeePerL2Gas extends GetterInstruction { @@ -129,9 +114,6 @@ export class FeePerL2Gas extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.feePerL2Gas; } - public static deserialize(buf: BufferCursor | Buffer): FeePerL2Gas { - return new FeePerL2Gas(...GetterInstruction.deserializeBase(buf)); - } } export class FeePerDAGas extends GetterInstruction { @@ -144,9 +126,6 @@ export class FeePerDAGas extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.feePerDaGas; } - public static deserialize(buf: BufferCursor | Buffer): FeePerDAGas { - return new FeePerDAGas(...GetterInstruction.deserializeBase(buf)); - } } export class Portal extends GetterInstruction { @@ -159,9 +138,6 @@ export class Portal extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.portal.toField(); } - public static deserialize(buf: BufferCursor | Buffer): Portal { - return new Portal(...GetterInstruction.deserializeBase(buf)); - } } export class ChainId extends GetterInstruction { @@ -174,9 +150,6 @@ export class ChainId extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.globals.chainId; } - public static deserialize(buf: BufferCursor | Buffer): ChainId { - return new ChainId(...GetterInstruction.deserializeBase(buf)); - } } export class Version extends GetterInstruction { @@ -189,9 +162,6 @@ export class Version extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.globals.version; } - public static deserialize(buf: BufferCursor | Buffer): Version { - return new Version(...GetterInstruction.deserializeBase(buf)); - } } export class BlockNumber extends GetterInstruction { @@ -204,9 +174,6 @@ export class BlockNumber extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.globals.blockNumber; } - public static deserialize(buf: BufferCursor | Buffer): BlockNumber { - return new BlockNumber(...GetterInstruction.deserializeBase(buf)); - } } export class Timestamp extends GetterInstruction { @@ -219,9 +186,6 @@ export class Timestamp extends GetterInstruction { protected getIt(env: AvmExecutionEnvironment): any { return env.globals.timestamp; } - public static deserialize(buf: BufferCursor | Buffer): Timestamp { - return new Timestamp(...GetterInstruction.deserializeBase(buf)); - } } // export class Coinbase extends GetterInstruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 913300f5088..f4cb907d183 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -4,12 +4,10 @@ import { AvmContext } from '../avm_context.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, serialize, } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -19,7 +17,7 @@ export class Call extends Instruction { static readonly opcode: Opcode = Opcode.CALL; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: Call) => Call.opcode, OperandType.UINT8], [(c: Call) => c.indirect, OperandType.UINT8], [(c: Call) => c._gasOffset, OperandType.UINT32], @@ -44,11 +42,6 @@ export class Call extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Call { - const res = deserialize(buf, Call.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Call(...args); - } public serialize(): Buffer { return serialize(Call.wireFormat, this); @@ -90,7 +83,7 @@ export class StaticCall extends Instruction { static readonly opcode: Opcode = Opcode.STATICCALL; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_: StaticCall) => StaticCall.opcode, OperandType.UINT8], [(c: StaticCall) => c.indirect, OperandType.UINT8], [(c: StaticCall) => c._gasOffset, OperandType.UINT32], @@ -115,11 +108,6 @@ export class StaticCall extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): StaticCall { - const res = deserialize(buf, StaticCall.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new StaticCall(...args); - } public serialize(): Buffer { return serialize(StaticCall.wireFormat, this); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 0d66fd51bb1..eedf3f3aacb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,6 +1,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; +import { BufferCursor } from '../serialization/buffer_cursor.js'; +import { deserialize } from '../serialization/instruction_serialization.js'; export abstract class Instruction { public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; @@ -30,6 +32,12 @@ export abstract class Instruction { checkTag(machineState, tag, offset); } } + + public static deserialize; wireFormat: any}>(this: T, buf: BufferCursor | Buffer): InstanceType { + const res = deserialize(buf, this.wireFormat); + const args = res.slice(1) as ConstructorParameters; // Remove opcode. + return (new this(...args) as InstanceType); + } } /** diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts index 98e348100c9..8b8c54a96f5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -10,7 +10,7 @@ import { Instruction } from './instruction.js'; export abstract class TwoOperandInstruction extends Instruction { // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(c: TwoOperandInstruction) => c.opcode, OperandType.UINT8], [(c: TwoOperandInstruction) => c.indirect, OperandType.UINT8], [(c: TwoOperandInstruction) => c.inTag, OperandType.UINT8], @@ -42,7 +42,7 @@ export abstract class TwoOperandInstruction extends Instruction { export abstract class ThreeOperandInstruction extends Instruction { // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(c: ThreeOperandInstruction) => c.opcode, OperandType.UINT8], [(c: ThreeOperandInstruction) => c.indirect, OperandType.UINT8], [(c: ThreeOperandInstruction) => c.inTag, OperandType.UINT8], diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 6868e1c32f3..1c341916195 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,12 +1,10 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, serialize, } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -17,7 +15,7 @@ export class Set extends Instruction { static readonly opcode: Opcode = Opcode.SET; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: Set) => Set.opcode, OperandType.UINT8], [(c: Set) => c.indirect, OperandType.UINT8], [(c: Set) => c.inTag, OperandType.UINT8], @@ -29,11 +27,6 @@ export class Set extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Set { - const res = deserialize(buf, Set.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Set(...args); - } public serialize(): Buffer { return serialize(Set.wireFormat, this); @@ -53,7 +46,7 @@ export class CMov extends Instruction { static readonly opcode: Opcode = Opcode.CMOV; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: CMov) => CMov.opcode, OperandType.UINT8], [(c: CMov) => c.indirect, OperandType.UINT8], [(c: CMov) => c.aOffset, OperandType.UINT32], @@ -72,11 +65,6 @@ export class CMov extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): CMov { - const res = deserialize(buf, CMov.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new CMov(...args); - } public serialize(): Buffer { return serialize(CMov.wireFormat, this); @@ -106,10 +94,6 @@ export class Cast extends TwoOperandInstruction { return Cast.opcode; } - public static deserialize(buf: BufferCursor | Buffer): Cast { - const args = TwoOperandInstruction.deserializeBase(buf); - return new Cast(...args); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -129,7 +113,7 @@ export class Mov extends Instruction { static readonly opcode: Opcode = Opcode.MOV; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: Mov) => Mov.opcode, OperandType.UINT8], [(c: Mov) => c.indirect, OperandType.UINT8], [(c: Mov) => c.srcOffset, OperandType.UINT32], @@ -140,11 +124,6 @@ export class Mov extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): Mov { - const res = deserialize(buf, Mov.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Mov(...args); - } public serialize(): Buffer { return serialize(Mov.wireFormat, this); @@ -164,7 +143,7 @@ export class CalldataCopy extends Instruction { static readonly opcode: Opcode = Opcode.CALLDATACOPY; // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(_c: CalldataCopy) => CalldataCopy.opcode, OperandType.UINT8], [(c: CalldataCopy) => c.indirect, OperandType.UINT8], [(c: CalldataCopy) => c.cdOffset, OperandType.UINT32], @@ -176,11 +155,6 @@ export class CalldataCopy extends Instruction { super(); } - public static deserialize(buf: BufferCursor | Buffer): CalldataCopy { - const res = deserialize(buf, CalldataCopy.wireFormat); - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new CalldataCopy(...args); - } public serialize(): Buffer { return serialize(CalldataCopy.wireFormat, this); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index b75e2c9ddf6..1784cc85157 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -15,7 +15,7 @@ import { Instruction, InstructionExecutionError } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { // Instruction wire format with opcode. - private static readonly wireFormat: OperandPair[] = [ + static readonly wireFormat: OperandPair[] = [ [(c: BaseStorageInstruction) => c.opcode, OperandType.UINT8], [(c: BaseStorageInstruction) => c.indirect, OperandType.UINT8], [(c: BaseStorageInstruction) => c.aOffset, OperandType.UINT32], @@ -51,10 +51,6 @@ export class SStore extends BaseStorageInstruction { return SStore.opcode; } - public static deserialize(buf: BufferCursor | Buffer): SStore { - const args = BaseStorageInstruction.deserializeBase(buf); - return new SStore(...args); - } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { @@ -86,10 +82,6 @@ export class SLoad extends BaseStorageInstruction { return SLoad.opcode; } - public static deserialize(buf: BufferCursor | Buffer): SLoad { - const args = BaseStorageInstruction.deserializeBase(buf); - return new SLoad(...args); - } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const slot = machineState.memory.get(this.aOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts new file mode 100644 index 00000000000..03d7b40cda2 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts @@ -0,0 +1,25 @@ +import { EmitNoteHash } from "./accrued_substate.js"; + +describe("Testing alternative", () => { + + it("Test new serde", async () => { + + const arr: Uint8Array = Uint8Array.from([ + 47, + 1, + // + 32,32,32,32 + ]); + const buf = Buffer.from(arr); + + const opcode = EmitNoteHash.deserialize(buf); + console.log(opcode); + + + // const deserialized = EmitNoteHash2.deserialize(serialized); + + // expect(deserialized).toEqual(emitNoteHash); + }); + + +}); \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts index 3368dec18f6..2aa84170eee 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts @@ -130,3 +130,4 @@ export function serialize(operands: OperandPair[], cls: any): Buffer { return Buffer.concat(chunks); } + From e7c49b5124763a4e445af2dd1f54d437cc61d3ce Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:28:42 +0000 Subject: [PATCH 15/25] fix: move serialsation into the base class --- .../src/avm/opcodes/accrued_substate.ts | 14 ---------- .../acir-simulator/src/avm/opcodes/bitwise.ts | 1 - .../src/avm/opcodes/control_flow.ts | 26 +++++-------------- .../src/avm/opcodes/environment_getters.ts | 14 +--------- .../src/avm/opcodes/external_calls.ts | 12 ++------- .../src/avm/opcodes/instruction.ts | 9 ++++--- .../src/avm/opcodes/instruction_impl.ts | 2 ++ .../acir-simulator/src/avm/opcodes/memory.ts | 19 +++----------- .../acir-simulator/src/avm/opcodes/storage.ts | 17 ++---------- 9 files changed, 23 insertions(+), 91 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index 3453a8f2b93..629e36d92fc 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -4,7 +4,6 @@ import { Opcode, OperandPair, OperandType, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction, } from './instruction.js'; @@ -25,10 +24,6 @@ export class EmitNoteHash extends Instruction { super(); } - public serialize(): Buffer { - return serialize(EmitNoteHash.wireFormat, this); - } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -56,9 +51,6 @@ export class EmitNullifier extends Instruction { super(); } - public serialize(): Buffer { - return serialize(EmitNullifier.wireFormat, this); - } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { @@ -88,9 +80,6 @@ export class EmitUnencryptedLog extends Instruction { super(); } - public serialize(): Buffer { - return serialize(EmitUnencryptedLog.wireFormat, this); - } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { @@ -121,9 +110,6 @@ export class SendL2ToL1Message extends Instruction { } - public serialize(): Buffer { - return serialize(SendL2ToL1Message.wireFormat, this); - } async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 3cf18c3f891..71ff039c97f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,7 +1,6 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { IntegralValue } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js'; 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 c67737e9c83..29bd873c011 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -5,7 +5,6 @@ import { Opcode, OperandPair, OperandType, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; @@ -20,14 +19,12 @@ export class Return extends Instruction { [(c: Return) => c.returnOffset, OperandType.UINT32], [(c: Return) => c.copySize, OperandType.UINT32], ]; + wireFormat = Return.wireFormat; constructor(private indirect: number, private returnOffset: number, private copySize: number) { super(); } - public serialize(): Buffer { - return serialize(Return.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); @@ -49,15 +46,13 @@ export class Revert extends Instruction { [(c: Revert) => c.returnOffset, OperandType.UINT32], [(c: Revert) => c.retSize, OperandType.UINT32], ]; + wireFormat = Revert.wireFormat; constructor(private indirect: number, private returnOffset: number, private retSize: number) { super(); } - public serialize(): Buffer { - return serialize(Revert.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory @@ -78,15 +73,13 @@ export class Jump extends Instruction { [(_c: Jump) => Jump.opcode, OperandType.UINT8], [(c: Jump) => c.jumpOffset, OperandType.UINT32], ]; + wireFormat = Jump.wireFormat; constructor(private jumpOffset: number) { super(); } - public serialize(): Buffer { - return serialize(Jump.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.pc = this.jumpOffset; @@ -104,16 +97,13 @@ export class JumpI extends Instruction { [(c: JumpI) => c.loc, OperandType.UINT32], [(c: JumpI) => c.condOffset, OperandType.UINT32], ]; + wireFormat = JumpI.wireFormat; constructor(private indirect: number, private loc: number, private condOffset: number) { super(); } - public serialize(): Buffer { - return serialize(JumpI.wireFormat, this); - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const condition = machineState.memory.getAs(this.condOffset); @@ -135,15 +125,13 @@ export class InternalCall extends Instruction { [(_c: InternalCall) => InternalCall.opcode, OperandType.UINT8], [(c: InternalCall) => c.loc, OperandType.UINT32], ]; + wireFormat = InternalCall.wireFormat; constructor(private loc: number) { super(); } - public serialize(): Buffer { - return serialize(InternalCall.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.internalCallStack.push(machineState.pc + 1); @@ -159,14 +147,12 @@ export class InternalReturn extends Instruction { static readonly wireFormat: OperandPair[] = [ [(_c: InternalReturn) => InternalReturn.opcode, OperandType.UINT8], ]; + wireFormat = InternalReturn.wireFormat; constructor() { super(); } - public serialize(): Buffer { - return serialize(InternalReturn.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const jumpOffset = machineState.internalCallStack.pop(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index ed04370028b..4601d203f5b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -2,13 +2,10 @@ import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -19,21 +16,12 @@ abstract class GetterInstruction extends Instruction { [(c: GetterInstruction) => c.indirect, OperandType.UINT8], [(c: GetterInstruction) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = GetterInstruction.wireFormat; constructor(protected indirect: number, protected dstOffset: number) { super(); } - protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { - const res = deserialize(buf, GetterInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(GetterInstruction.wireFormat, this); - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const res = new Field(this.getIt(machineState.executionEnvironment)); machineState.memory.set(this.dstOffset, res); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index f4cb907d183..a115c615788 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -8,7 +8,6 @@ import { Opcode, OperandPair, OperandType, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; @@ -28,6 +27,7 @@ export class Call extends Instruction { [(c: Call) => c.retSize, OperandType.UINT32], [(c: Call) => c.successOffset, OperandType.UINT32], ]; + wireFormat = Call.wireFormat; constructor( private indirect: number, @@ -42,11 +42,6 @@ export class Call extends Instruction { super(); } - - public serialize(): Buffer { - return serialize(Call.wireFormat, this); - } - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const callAddress = machineState.memory.getAs(this.addrOffset); @@ -94,6 +89,7 @@ export class StaticCall extends Instruction { [(c: StaticCall) => c.retSize, OperandType.UINT32], [(c: StaticCall) => c.successOffset, OperandType.UINT32], ]; + wireFormat = StaticCall.wireFormat; constructor( private indirect: number, @@ -109,10 +105,6 @@ export class StaticCall extends Instruction { } - public serialize(): Buffer { - return serialize(StaticCall.wireFormat, this); - } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const callAddress = machineState.memory.get(this.addrOffset); const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index eedf3f3aacb..0a98ae391e3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -2,11 +2,10 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; -import { deserialize } from '../serialization/instruction_serialization.js'; +import { deserialize, serialize } from '../serialization/instruction_serialization.js'; export abstract class Instruction { public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; - public abstract serialize(): Buffer; incrementPc(machineState: AvmMachineState): void { machineState.pc++; @@ -36,7 +35,11 @@ export abstract class Instruction { public static deserialize; wireFormat: any}>(this: T, buf: BufferCursor | Buffer): InstanceType { const res = deserialize(buf, this.wireFormat); const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return (new this(...args) as InstanceType); + return new this(...args); + } + + public serialize(this: T): Buffer { + return serialize(this.wireFormat, this); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts index 8b8c54a96f5..0c852cf1a81 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -17,6 +17,7 @@ export abstract class TwoOperandInstruction extends Instruction { [(c: TwoOperandInstruction) => c.aOffset, OperandType.UINT32], [(c: TwoOperandInstruction) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = TwoOperandInstruction.wireFormat; constructor( protected indirect: number, @@ -50,6 +51,7 @@ export abstract class ThreeOperandInstruction extends Instruction { [(c: ThreeOperandInstruction) => c.bOffset, OperandType.UINT32], [(c: ThreeOperandInstruction) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = ThreeOperandInstruction.wireFormat; constructor( protected indirect: number, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 1c341916195..03e53036843 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -5,7 +5,6 @@ import { Opcode, OperandPair, OperandType, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { TwoOperandInstruction } from './instruction_impl.js'; @@ -22,16 +21,12 @@ export class Set extends Instruction { [(c: Set) => c.value, OperandType.UINT128], [(c: Set) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = Set.wireFormat; constructor(private indirect: number, private inTag: number, private value: bigint, private dstOffset: number) { super(); } - - public serialize(): Buffer { - return serialize(Set.wireFormat, this); - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const res = TaggedMemory.integralFromTag(this.value, this.inTag); @@ -54,6 +49,7 @@ export class CMov extends Instruction { [(c: CMov) => c.condOffset, OperandType.UINT32], [(c: CMov) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = CMov.wireFormat; constructor( private indirect: number, @@ -66,9 +62,6 @@ export class CMov extends Instruction { } - public serialize(): Buffer { - return serialize(CMov.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -119,15 +112,13 @@ export class Mov extends Instruction { [(c: Mov) => c.srcOffset, OperandType.UINT32], [(c: Mov) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = Mov.wireFormat; constructor(private indirect: number, private srcOffset: number, private dstOffset: number) { super(); } - public serialize(): Buffer { - return serialize(Mov.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.srcOffset); @@ -150,15 +141,13 @@ export class CalldataCopy extends Instruction { [(c: CalldataCopy) => c.copySize, OperandType.UINT32], [(c: CalldataCopy) => c.dstOffset, OperandType.UINT32], ]; + wireFormat = CalldataCopy.wireFormat; constructor(private indirect: number, private cdOffset: number, private copySize: number, private dstOffset: number) { super(); } - public serialize(): Buffer { - return serialize(CalldataCopy.wireFormat, this); - } async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const transformedData = machineState.executionEnvironment.calldata diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index 1784cc85157..d0691b604a7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -3,39 +3,27 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; import { Opcode, OperandPair, OperandType, - deserialize, - serialize, } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ + public static readonly wireFormat: OperandPair[] = [ [(c: BaseStorageInstruction) => c.opcode, OperandType.UINT8], [(c: BaseStorageInstruction) => c.indirect, OperandType.UINT8], [(c: BaseStorageInstruction) => c.aOffset, OperandType.UINT32], [(c: BaseStorageInstruction) => c.bOffset, OperandType.UINT32], ]; + wireFormat = BaseStorageInstruction.wireFormat; constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) { super(); } - protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { - const res = deserialize(buf, BaseStorageInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(BaseStorageInstruction.wireFormat, this); - } - protected abstract get opcode(): Opcode; } @@ -51,7 +39,6 @@ export class SStore extends BaseStorageInstruction { return SStore.opcode; } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); From 5640b6a14fde4c66aab28a80367704fa85e65800 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:07:41 +0000 Subject: [PATCH 16/25] fix: simplify wire format --- .../src/avm/opcodes/accrued_substate.ts | 39 ++--------- .../src/avm/opcodes/arithmetic.ts | 4 -- .../acir-simulator/src/avm/opcodes/bitwise.ts | 6 -- .../src/avm/opcodes/comparators.ts | 2 - .../src/avm/opcodes/control_flow.ts | 65 ++++++------------- .../src/avm/opcodes/environment_getters.ts | 13 +--- .../src/avm/opcodes/external_calls.ts | 49 ++++++-------- .../src/avm/opcodes/instruction.ts | 9 ++- .../src/avm/opcodes/instruction_impl.ts | 36 ++++------ .../acir-simulator/src/avm/opcodes/memory.ts | 65 +++++++------------ .../acir-simulator/src/avm/opcodes/storage.ts | 18 ++--- .../src/avm/opcodes/tinker.test.ts | 41 +++++------- .../instruction_serialization.test.ts | 20 +++--- .../instruction_serialization.ts | 17 +++-- 14 files changed, 133 insertions(+), 251 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index 629e36d92fc..6c5a5c9128b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -1,12 +1,7 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmJournal } from '../journal/journal.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; -import { Instruction, -} from './instruction.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; +import { Instruction } from './instruction.js'; import { StaticCallStorageAlterError } from './storage.js'; export class EmitNoteHash extends Instruction { @@ -14,11 +9,7 @@ export class EmitNoteHash extends Instruction { static readonly opcode: Opcode = Opcode.EMITNOTEHASH; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: EmitNoteHash) => EmitNoteHash.opcode, OperandType.UINT8], - [(c: EmitNoteHash) => c.indirect, OperandType.UINT8], - [(c: EmitNoteHash) => c.noteHashOffset, OperandType.UINT32], - ]; + static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(private indirect: number, private noteHashOffset: number) { super(); @@ -41,17 +32,12 @@ export class EmitNullifier extends Instruction { static readonly opcode: Opcode = Opcode.EMITNULLIFIER; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: EmitNullifier) => EmitNullifier.opcode, OperandType.UINT8], - [(c: EmitNullifier) => c.indirect, OperandType.UINT8], - [(c: EmitNullifier) => c.nullifierOffset, OperandType.UINT32], - ]; + static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(private indirect: number, private nullifierOffset: number) { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -69,18 +55,12 @@ export class EmitUnencryptedLog extends Instruction { static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: EmitUnencryptedLog) => EmitUnencryptedLog.opcode, OperandType.UINT8], - [(c: EmitUnencryptedLog) => c.indirect, OperandType.UINT8], - [(c: EmitUnencryptedLog) => c.logOffset, OperandType.UINT32], - [(c: EmitUnencryptedLog) => c.logSize, OperandType.UINT32], - ]; + static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32]; constructor(private indirect: number, private logOffset: number, private logSize: number) { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -98,19 +78,12 @@ export class SendL2ToL1Message extends Instruction { static readonly opcode: Opcode = Opcode.SENDL2TOL1MSG; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: SendL2ToL1Message) => SendL2ToL1Message.opcode, OperandType.UINT8], - [(c: SendL2ToL1Message) => c.indirect, OperandType.UINT8], - [(c: SendL2ToL1Message) => c.msgOffset, OperandType.UINT32], - [(c: SendL2ToL1Message) => c.msgSize, OperandType.UINT32], - ]; + static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32]; constructor(private indirect: number, private msgOffset: number, private msgSize: number) { super(); } - - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index b2a8b7d5cca..602cbeed721 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -15,7 +15,6 @@ export class Add extends ThreeOperandInstruction { return Add.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -39,7 +38,6 @@ export class Sub extends ThreeOperandInstruction { return Sub.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -63,7 +61,6 @@ export class Mul extends ThreeOperandInstruction { return Mul.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -87,7 +84,6 @@ export class Div extends ThreeOperandInstruction { return Div.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 71ff039c97f..a21831d6ee8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -17,7 +17,6 @@ export class And extends ThreeOperandInstruction { return And.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -43,7 +42,6 @@ export class Or extends ThreeOperandInstruction { return Or.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -69,7 +67,6 @@ export class Xor extends ThreeOperandInstruction { return Xor.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -95,7 +92,6 @@ export class Not extends TwoOperandInstruction { return Not.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset); @@ -120,7 +116,6 @@ export class Shl extends ThreeOperandInstruction { return Shl.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -146,7 +141,6 @@ export class Shr extends ThreeOperandInstruction { return Shr.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 555faaabdd6..ea01d1b83b7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -16,7 +16,6 @@ export class Eq extends ThreeOperandInstruction { return Eq.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -69,7 +68,6 @@ export class Lte extends ThreeOperandInstruction { return Lte.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); 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 29bd873c011..2859611e2bb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,11 +1,7 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { IntegralValue } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; export class Return extends Instruction { @@ -13,19 +9,17 @@ export class Return extends Instruction { static readonly opcode: Opcode = Opcode.RETURN; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: Return) => Return.opcode, OperandType.UINT8], - [(c: Return) => c.indirect, OperandType.UINT8], - [(c: Return) => c.returnOffset, OperandType.UINT32], - [(c: Return) => c.copySize, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = Return.wireFormat; constructor(private indirect: number, private returnOffset: number, private copySize: number) { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); @@ -40,20 +34,17 @@ export class Revert extends Instruction { static readonly opcode: Opcode = Opcode.REVERT; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: Revert) => Revert.opcode, OperandType.UINT8], - [(c: Revert) => c.indirect, OperandType.UINT8], - [(c: Revert) => c.returnOffset, OperandType.UINT32], - [(c: Revert) => c.retSize, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = Revert.wireFormat; constructor(private indirect: number, private returnOffset: number, private retSize: number) { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const returnData = machineState.memory .getSlice(this.returnOffset, this.returnOffset + this.retSize) @@ -69,18 +60,12 @@ export class Jump extends Instruction { static readonly opcode: Opcode = Opcode.JUMP; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: Jump) => Jump.opcode, OperandType.UINT8], - [(c: Jump) => c.jumpOffset, OperandType.UINT32], - ]; - wireFormat = Jump.wireFormat; + static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT32]; constructor(private jumpOffset: number) { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.pc = this.jumpOffset; } @@ -91,19 +76,17 @@ export class JumpI extends Instruction { static readonly opcode: Opcode = Opcode.JUMPI; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: JumpI) => JumpI.opcode, OperandType.UINT8], - [(c: JumpI) => c.indirect, OperandType.UINT8], - [(c: JumpI) => c.loc, OperandType.UINT32], - [(c: JumpI) => c.condOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = JumpI.wireFormat; constructor(private indirect: number, private loc: number, private condOffset: number) { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const condition = machineState.memory.getAs(this.condOffset); @@ -121,18 +104,12 @@ export class InternalCall extends Instruction { static readonly opcode: Opcode = Opcode.INTERNALCALL; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: InternalCall) => InternalCall.opcode, OperandType.UINT8], - [(c: InternalCall) => c.loc, OperandType.UINT32], - ]; - wireFormat = InternalCall.wireFormat; + static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT32]; constructor(private loc: number) { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.internalCallStack.push(machineState.pc + 1); machineState.pc = this.loc; @@ -144,16 +121,12 @@ export class InternalReturn extends Instruction { static readonly opcode: Opcode = Opcode.INTERNALRETURN; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: InternalReturn) => InternalReturn.opcode, OperandType.UINT8], - ]; - wireFormat = InternalReturn.wireFormat; + static readonly wireFormat: OperandType[] = [OperandType.UINT8]; constructor() { super(); } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const jumpOffset = machineState.internalCallStack.pop(); if (jumpOffset === undefined) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index 4601d203f5b..591fcb8303f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -2,21 +2,12 @@ import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; abstract class GetterInstruction extends Instruction { // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(c: GetterInstruction) => c.opcode, OperandType.UINT8], - [(c: GetterInstruction) => c.indirect, OperandType.UINT8], - [(c: GetterInstruction) => c.dstOffset, OperandType.UINT32], - ]; - wireFormat = GetterInstruction.wireFormat; + static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(protected indirect: number, protected dstOffset: number) { super(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index a115c615788..3caff774d1b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -4,11 +4,7 @@ import { AvmContext } from '../avm_context.js'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; export class Call extends Instruction { @@ -16,18 +12,17 @@ export class Call extends Instruction { static readonly opcode: Opcode = Opcode.CALL; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: Call) => Call.opcode, OperandType.UINT8], - [(c: Call) => c.indirect, OperandType.UINT8], - [(c: Call) => c._gasOffset, OperandType.UINT32], - [(c: Call) => c.addrOffset, OperandType.UINT32], - [(c: Call) => c.argsOffset, OperandType.UINT32], - [(c: Call) => c.argsSize, OperandType.UINT32], - [(c: Call) => c.retOffset, OperandType.UINT32], - [(c: Call) => c.retSize, OperandType.UINT32], - [(c: Call) => c.successOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = Call.wireFormat; constructor( private indirect: number, @@ -78,18 +73,17 @@ export class StaticCall extends Instruction { static readonly opcode: Opcode = Opcode.STATICCALL; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_: StaticCall) => StaticCall.opcode, OperandType.UINT8], - [(c: StaticCall) => c.indirect, OperandType.UINT8], - [(c: StaticCall) => c._gasOffset, OperandType.UINT32], - [(c: StaticCall) => c.addrOffset, OperandType.UINT32], - [(c: StaticCall) => c.argsOffset, OperandType.UINT32], - [(c: StaticCall) => c.argsSize, OperandType.UINT32], - [(c: StaticCall) => c.retOffset, OperandType.UINT32], - [(c: StaticCall) => c.retSize, OperandType.UINT32], - [(c: StaticCall) => c.successOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = StaticCall.wireFormat; constructor( private indirect: number, @@ -104,7 +98,6 @@ export class StaticCall extends Instruction { super(); } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const callAddress = machineState.memory.get(this.addrOffset); const calldata = machineState.memory.getSlice(this.argsOffset, this.argsSize).map(f => new Fr(f.toBigInt())); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 0a98ae391e3..d6554d72140 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -32,14 +32,17 @@ export abstract class Instruction { } } - public static deserialize; wireFormat: any}>(this: T, buf: BufferCursor | Buffer): InstanceType { + public static deserialize; wireFormat: any }>( + this: T, + buf: BufferCursor | Buffer, + ): InstanceType { const res = deserialize(buf, this.wireFormat); const args = res.slice(1) as ConstructorParameters; // Remove opcode. return new this(...args); } - public serialize(this: T): Buffer { - return serialize(this.wireFormat, this); + public serialize(this: any): Buffer { + return serialize(this.constructor.wireFormat, this); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts index 0c852cf1a81..2ccc4a33251 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -1,23 +1,16 @@ import { BufferCursor } from '../serialization/buffer_cursor.js'; -import { - Opcode, - OperandPair, - OperandType, - deserialize, - serialize, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; export abstract class TwoOperandInstruction extends Instruction { // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(c: TwoOperandInstruction) => c.opcode, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.indirect, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.inTag, OperandType.UINT8], - [(c: TwoOperandInstruction) => c.aOffset, OperandType.UINT32], - [(c: TwoOperandInstruction) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = TwoOperandInstruction.wireFormat; constructor( protected indirect: number, @@ -43,15 +36,14 @@ export abstract class TwoOperandInstruction extends Instruction { export abstract class ThreeOperandInstruction extends Instruction { // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(c: ThreeOperandInstruction) => c.opcode, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.indirect, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.inTag, OperandType.UINT8], - [(c: ThreeOperandInstruction) => c.aOffset, OperandType.UINT32], - [(c: ThreeOperandInstruction) => c.bOffset, OperandType.UINT32], - [(c: ThreeOperandInstruction) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = ThreeOperandInstruction.wireFormat; constructor( protected indirect: number, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 03e53036843..927e3f5bec1 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,11 +1,7 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; import { TwoOperandInstruction } from './instruction_impl.js'; @@ -14,14 +10,13 @@ export class Set extends Instruction { static readonly opcode: Opcode = Opcode.SET; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: Set) => Set.opcode, OperandType.UINT8], - [(c: Set) => c.indirect, OperandType.UINT8], - [(c: Set) => c.inTag, OperandType.UINT8], - [(c: Set) => c.value, OperandType.UINT128], - [(c: Set) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT128, + OperandType.UINT32, ]; - wireFormat = Set.wireFormat; constructor(private indirect: number, private inTag: number, private value: bigint, private dstOffset: number) { super(); @@ -41,15 +36,14 @@ export class CMov extends Instruction { static readonly opcode: Opcode = Opcode.CMOV; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: CMov) => CMov.opcode, OperandType.UINT8], - [(c: CMov) => c.indirect, OperandType.UINT8], - [(c: CMov) => c.aOffset, OperandType.UINT32], - [(c: CMov) => c.bOffset, OperandType.UINT32], - [(c: CMov) => c.condOffset, OperandType.UINT32], - [(c: CMov) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = CMov.wireFormat; constructor( private indirect: number, @@ -61,8 +55,6 @@ export class CMov extends Instruction { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -87,7 +79,6 @@ export class Cast extends TwoOperandInstruction { return Cast.opcode; } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -106,20 +97,17 @@ export class Mov extends Instruction { static readonly opcode: Opcode = Opcode.MOV; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: Mov) => Mov.opcode, OperandType.UINT8], - [(c: Mov) => c.indirect, OperandType.UINT8], - [(c: Mov) => c.srcOffset, OperandType.UINT32], - [(c: Mov) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = Mov.wireFormat; constructor(private indirect: number, private srcOffset: number, private dstOffset: number) { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.srcOffset); @@ -134,21 +122,18 @@ export class CalldataCopy extends Instruction { static readonly opcode: Opcode = Opcode.CALLDATACOPY; // Instruction wire format with opcode. - static readonly wireFormat: OperandPair[] = [ - [(_c: CalldataCopy) => CalldataCopy.opcode, OperandType.UINT8], - [(c: CalldataCopy) => c.indirect, OperandType.UINT8], - [(c: CalldataCopy) => c.cdOffset, OperandType.UINT32], - [(c: CalldataCopy) => c.copySize, OperandType.UINT32], - [(c: CalldataCopy) => c.dstOffset, OperandType.UINT32], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = CalldataCopy.wireFormat; constructor(private indirect: number, private cdOffset: number, private copySize: number, private dstOffset: number) { super(); } - - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const transformedData = machineState.executionEnvironment.calldata .slice(this.cdOffset, this.cdOffset + this.copySize) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index d0691b604a7..5bf5aae63c6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -3,22 +3,17 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmMachineState } from '../avm_machine_state.js'; import { Field } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/journal.js'; -import { - Opcode, - OperandPair, - OperandType, -} from '../serialization/instruction_serialization.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Instruction, InstructionExecutionError } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { // Instruction wire format with opcode. - public static readonly wireFormat: OperandPair[] = [ - [(c: BaseStorageInstruction) => c.opcode, OperandType.UINT8], - [(c: BaseStorageInstruction) => c.indirect, OperandType.UINT8], - [(c: BaseStorageInstruction) => c.aOffset, OperandType.UINT32], - [(c: BaseStorageInstruction) => c.bOffset, OperandType.UINT32], + public static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, ]; - wireFormat = BaseStorageInstruction.wireFormat; constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) { super(); @@ -69,7 +64,6 @@ export class SLoad extends BaseStorageInstruction { return SLoad.opcode; } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const slot = machineState.memory.get(this.aOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts index 03d7b40cda2..8390bb1b7dd 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts @@ -1,25 +1,16 @@ -import { EmitNoteHash } from "./accrued_substate.js"; - -describe("Testing alternative", () => { - - it("Test new serde", async () => { - - const arr: Uint8Array = Uint8Array.from([ - 47, - 1, - // - 32,32,32,32 - ]); - const buf = Buffer.from(arr); - - const opcode = EmitNoteHash.deserialize(buf); - console.log(opcode); - - - // const deserialized = EmitNoteHash2.deserialize(serialized); - - // expect(deserialized).toEqual(emitNoteHash); - }); - - -}); \ No newline at end of file +import { EmitNoteHash } from './accrued_substate.js'; + +describe('Testing alternative', () => { + it('Test new serde', async () => { + const arr: Uint8Array = Uint8Array.from([ + 47, 1, + // + 32, 32, 32, 32, + ]); + + const buf = Buffer.from(arr); + const opcode = EmitNoteHash.deserialize(buf); + const serialized = opcode.serialize(); + expect(serialized).toEqual(buf); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts index 6bb0d4e30d6..3f7d8905c98 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.test.ts @@ -1,22 +1,22 @@ import { BufferCursor } from './buffer_cursor.js'; -import { OperandPair, OperandType, deserialize, serialize } from './instruction_serialization.js'; +import { OperandType, deserialize, serialize } from './instruction_serialization.js'; class InstA { constructor(private a: number, private b: number, private c: number, private d: bigint, private e: bigint) {} static readonly opcode: number = 1; - static readonly wireFormat: OperandPair[] = [ - [(_: InstA) => InstA.opcode, OperandType.UINT8], - [(c: InstA) => c.a, OperandType.UINT8], - [(c: InstA) => c.b, OperandType.UINT16], - [(c: InstA) => c.c, OperandType.UINT32], - [(c: InstA) => c.d, OperandType.UINT64], - [(c: InstA) => c.e, OperandType.UINT128], + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT16, + OperandType.UINT32, + OperandType.UINT64, + OperandType.UINT128, ]; } describe('Instruction Serialization', () => { - it('Should serialize all types from OperandPair[]', () => { + it('Should serialize all types from OperandType[]', () => { const instance = new InstA(0x12, 0x1234, 0x12345678, 0x1234567887654321n, 0x1234567887654321abcdef0000fedcban); const actual: Buffer = serialize(InstA.wireFormat, instance); @@ -41,7 +41,7 @@ describe('Instruction Serialization', () => { ); }); - it('Should deserialize all types from OperandPair[]', () => { + it('Should deserialize all types from OperandType[]', () => { const buffer = Buffer.from( [ // opcode diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts index 2aa84170eee..33ac64668c0 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts @@ -73,8 +73,6 @@ export enum OperandType { UINT128, } -export type OperandPair = [(c: any) => any, OperandType]; - function readBigInt128BE(this: Buffer): bigint { const totalBytes = 16; let ret: bigint = 0n; @@ -101,14 +99,14 @@ const OPERAND_SPEC = new Map any, (value: any) => an [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], ]); -export function deserialize(cursor: BufferCursor | Buffer, operands: OperandPair[]): any[] { +export function deserialize(cursor: BufferCursor | Buffer, operands: OperandType[]): any[] { const argValues = []; if (cursor instanceof Buffer) { cursor = new BufferCursor(cursor); } for (const op of operands) { - const [_opGetter, opType] = op; + const opType = op; const [sizeBytes, reader, _writer] = OPERAND_SPEC.get(opType)!; argValues.push(reader.call(cursor.bufferAtPosition())); cursor.advance(sizeBytes); @@ -117,17 +115,18 @@ export function deserialize(cursor: BufferCursor | Buffer, operands: OperandPair return argValues; } -export function serialize(operands: OperandPair[], cls: any): Buffer { +export function serialize(operands: OperandType[], cls: any): Buffer { const chunks: Buffer[] = []; - for (const op of operands) { - const [opGetter, opType] = op; + // TODO: infer opcode not in this loop + const classValues = [cls.constructor.opcode, ...Object.values(cls)]; + for (let i = 0; i < operands.length; i++) { + const opType = operands[i]; const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; const buf = Buffer.alloc(sizeBytes); - writer.call(buf, opGetter(cls)); + writer.call(buf, classValues[i]); chunks.push(buf); } return Buffer.concat(chunks); } - From fafdea2ce2156778edac5b4966e37ccb703d43d1 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:14:59 +0000 Subject: [PATCH 17/25] chore: remove temp tinker test --- .../src/avm/opcodes/tinker.test.ts | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts diff --git a/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts deleted file mode 100644 index 8390bb1b7dd..00000000000 --- a/yarn-project/acir-simulator/src/avm/opcodes/tinker.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { EmitNoteHash } from './accrued_substate.js'; - -describe('Testing alternative', () => { - it('Test new serde', async () => { - const arr: Uint8Array = Uint8Array.from([ - 47, 1, - // - 32, 32, 32, 32, - ]); - - const buf = Buffer.from(arr); - const opcode = EmitNoteHash.deserialize(buf); - const serialized = opcode.serialize(); - expect(serialized).toEqual(buf); - }); -}); From 7472d41c62f986c96f74f1d0a88e2f2689726bbd Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 10:04:20 +0000 Subject: [PATCH 18/25] minor changes --- .../avm/opcodes/environment_getters.test.ts | 42 ++++++- .../bytecode_serialization.test.ts | 4 +- .../instruction_serialization.ts | 118 +++++++++--------- 3 files changed, 104 insertions(+), 60 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 965fa753d5b..4198b8e4850 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -37,9 +37,45 @@ describe('Environment getters instructions', () => { expect(actual).toEqual(value); }; - it('Should read address correctly', async () => { - const address = new Fr(123456n); - await envGetterTest('address', address, new Address(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('ADDRESS', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + // opcode + Address.opcode, + // indirect + 0x01, + // dstOffset + 0x12, + 0x34, + 0x56, + 0x78, + ]); + + const inst = Address.deserialize(buf); + expect(inst).toEqual(new Address(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Address(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + // opcode + Address.opcode, + // indirect + 0x01, + // dstOffset + 0x12, + 0x34, + 0x56, + 0x78, + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('address', address, new Address(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); it('Should read storage address correctly', async () => { diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts index 145ad283910..adca61ef609 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.test.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; -import { Add, Sub } from '../opcodes/index.js'; +import { Add, Address, Sub } from '../opcodes/index.js'; import { BufferCursor } from './buffer_cursor.js'; import { InstructionSet, decodeFromBytecode, encodeToBytecode } from './bytecode_serialization.js'; import { Opcode } from './instruction_serialization.js'; @@ -74,6 +74,7 @@ describe('Bytecode Serialization', () => { const instructions = [ new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Address(/*indirect=*/ 0, /*dstOffset=*/ 1), ]; const bytecode = Buffer.concat(instructions.map(i => i.serialize())); @@ -86,6 +87,7 @@ describe('Bytecode Serialization', () => { const instructions = [ new Add(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Sub(/*indirect=*/ 0, /*inTag=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), + new Address(/*indirect=*/ 0, /*dstOffset=*/ 1), ]; const actual = encodeToBytecode(instructions); diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts index 33ac64668c0..2e847f84466 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts @@ -1,3 +1,5 @@ +import { strict as assert } from 'assert'; + import { BufferCursor } from './buffer_cursor.js'; /** @@ -5,62 +7,62 @@ import { BufferCursor } from './buffer_cursor.js'; * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set */ export enum Opcode { - ADD = 0x00, - SUB = 0x01, - MUL = 0x02, - DIV = 0x03, - EQ = 0x04, - LT = 0x05, - LTE = 0x06, - AND = 0x07, - OR = 0x08, - XOR = 0x09, - NOT = 0x0a, - SHL = 0x0b, - SHR = 0x0c, - CAST = 0x0d, - ADDRESS = 0x0e, - STORAGEADDRESS = 0x0f, - ORIGIN = 0x10, - SENDER = 0x11, - PORTAL = 0x12, - FEEPERL1GAS = 0x13, - FEEPERL2GAS = 0x14, - FEEPERDAGAS = 0x15, - CONTRACTCALLDEPTH = 0x16, - CHAINID = 0x17, - VERSION = 0x18, - BLOCKNUMBER = 0x19, - TIMESTAMP = 0x1a, - COINBASE = 0x1b, - BLOCKL1GASLIMIT = 0x1c, - BLOCKL2GASLIMIT = 0x1d, - BLOCKDAGASLIMIT = 0x1e, - CALLDATACOPY = 0x1f, - L1GASLEFT = 0x20, - L2GASLEFT = 0x21, - DAGASLEFT = 0x22, - JUMP = 0x23, - JUMPI = 0x24, - INTERNALCALL = 0x25, - INTERNALRETURN = 0x26, - SET = 0x27, - MOV = 0x28, - CMOV = 0x29, - BLOCKHEADERBYNUMBER = 0x2a, - SLOAD = 0x2b, // Public Storage - SSTORE = 0x2c, // Public Storage - READL1TOL2MSG = 0x2d, // Messages - SENDL2TOL1MSG = 0x2e, // Messages - EMITNOTEHASH = 0x2f, // Notes & Nullifiers - EMITNULLIFIER = 0x30, // Notes & Nullifiers - EMITUNENCRYPTEDLOG = 0x31, - CALL = 0x32, - STATICCALL = 0x33, - RETURN = 0x34, - REVERT = 0x35, - KECCAK = 0x36, - POSEIDON = 0x37, + ADD, + SUB, + MUL, + DIV, + EQ, + LT, + LTE, + AND, + OR, + XOR, + NOT, + SHL, + SHR, + CAST, + ADDRESS, + STORAGEADDRESS, + ORIGIN, + SENDER, + PORTAL, + FEEPERL1GAS, + FEEPERL2GAS, + FEEPERDAGAS, + CONTRACTCALLDEPTH, + CHAINID, + VERSION, + BLOCKNUMBER, + TIMESTAMP, + COINBASE, + BLOCKL1GASLIMIT, + BLOCKL2GASLIMIT, + BLOCKDAGASLIMIT, + CALLDATACOPY, + L1GASLEFT, + L2GASLEFT, + DAGASLEFT, + JUMP, + JUMPI, + INTERNALCALL, + INTERNALRETURN, + SET, + MOV, + CMOV, + BLOCKHEADERBYNUMBER, + SLOAD, // Public Storage + SSTORE, // Public Storage + READL1TOL2MSG, // Messages + SENDL2TOL1MSG, // Messages + EMITNOTEHASH, // Notes & Nullifiers + EMITNULLIFIER, // Notes & Nullifiers + EMITUNENCRYPTEDLOG, + CALL, + STATICCALL, + RETURN, + REVERT, + KECCAK, + POSEIDON, // Add new opcodes before this TOTAL_OPCODES_NUMBER, } @@ -120,6 +122,10 @@ export function serialize(operands: OperandType[], cls: any): Buffer { // TODO: infer opcode not in this loop const classValues = [cls.constructor.opcode, ...Object.values(cls)]; + assert( + classValues.length === operands.length, + `Got ${classValues.length} values but only ${operands.length} serialization operands are specified!`, + ); for (let i = 0; i < operands.length; i++) { const opType = operands[i]; const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; From 08b1938cc3f44b0f97a3f56fb963fd987796775a Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 10:41:58 +0000 Subject: [PATCH 19/25] fix noir test compilation: still failing --- yarn-project/acir-simulator/src/avm/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 47eaec04087..aaa7dc1b874 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -46,7 +46,7 @@ describe('avm', () => { const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!; // Decode bytecode into instructions - const instructions = decodeBytecode(Buffer.from(addArtifact.bytecode, 'base64')); + const instructions = decodeFromBytecode(Buffer.from(addArtifact.bytecode, 'base64')); // Execute instructions const context = new AvmMachineState(initExecutionEnvironment({ calldata })); From e107cf582ec5b53f8eb0a8ae00edfd304d29c6e6 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 13:11:46 +0000 Subject: [PATCH 20/25] fix docs, formatting, and remove "get opcode" not necessary thanks to what Maddiaa did --- .../src/avm/opcodes/accrued_substate.ts | 12 ++--- .../src/avm/opcodes/arithmetic.ts | 16 ------- .../acir-simulator/src/avm/opcodes/bitwise.ts | 24 ---------- .../src/avm/opcodes/comparators.ts | 12 ----- .../src/avm/opcodes/control_flow.ts | 15 ++---- .../src/avm/opcodes/environment_getters.ts | 39 +-------------- .../src/avm/opcodes/external_calls.ts | 6 +-- .../src/avm/opcodes/instruction.ts | 14 +++++- .../src/avm/opcodes/instruction_impl.ts | 39 +++++---------- .../acir-simulator/src/avm/opcodes/memory.ts | 16 ++----- .../acir-simulator/src/avm/opcodes/storage.ts | 12 +---- .../serialization/bytecode_serialization.ts | 10 ++-- .../instruction_serialization.ts | 48 ++++++++++++++----- 13 files changed, 80 insertions(+), 183 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts index 6c5a5c9128b..00c78f6879f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -7,8 +7,7 @@ import { StaticCallStorageAlterError } from './storage.js'; export class EmitNoteHash extends Instruction { static type: string = 'EMITNOTEHASH'; static readonly opcode: Opcode = Opcode.EMITNOTEHASH; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(private indirect: number, private noteHashOffset: number) { @@ -30,8 +29,7 @@ export class EmitNoteHash extends Instruction { export class EmitNullifier extends Instruction { static type: string = 'EMITNULLIFIER'; static readonly opcode: Opcode = Opcode.EMITNULLIFIER; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(private indirect: number, private nullifierOffset: number) { @@ -53,8 +51,7 @@ export class EmitNullifier extends Instruction { export class EmitUnencryptedLog extends Instruction { static type: string = 'EMITUNENCRYPTEDLOG'; static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32]; constructor(private indirect: number, private logOffset: number, private logSize: number) { @@ -76,8 +73,7 @@ export class EmitUnencryptedLog extends Instruction { export class SendL2ToL1Message extends Instruction { static type: string = 'EMITUNENCRYPTEDLOG'; static readonly opcode: Opcode = Opcode.SENDL2TOL1MSG; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32, OperandType.UINT32]; constructor(private indirect: number, private msgOffset: number, private msgSize: number) { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 602cbeed721..71a571b45dd 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -11,10 +11,6 @@ export class Add extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Add.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -34,10 +30,6 @@ export class Sub extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Sub.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -57,10 +49,6 @@ export class Mul extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Mul.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); @@ -80,10 +68,6 @@ export class Div extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Div.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index a21831d6ee8..f30be9b5053 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -13,10 +13,6 @@ export class And extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return And.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -38,10 +34,6 @@ export class Or extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Or.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -63,10 +55,6 @@ export class Xor extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Xor.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -88,10 +76,6 @@ export class Not extends TwoOperandInstruction { super(indirect, inTag, aOffset, dstOffset); } - protected get opcode() { - return Not.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset); @@ -112,10 +96,6 @@ export class Shl extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Shl.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -137,10 +117,6 @@ export class Shr extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Shr.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index ea01d1b83b7..a4cffa19d4a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -12,10 +12,6 @@ export class Eq extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Eq.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -38,10 +34,6 @@ export class Lt extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Lt.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); @@ -64,10 +56,6 @@ export class Lte extends ThreeOperandInstruction { super(indirect, inTag, aOffset, bOffset, dstOffset); } - protected get opcode() { - return Lte.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); 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 2859611e2bb..1527a677116 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -7,8 +7,7 @@ import { Instruction, InstructionExecutionError } from './instruction.js'; export class Return extends Instruction { static type: string = 'RETURN'; static readonly opcode: Opcode = Opcode.RETURN; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -32,8 +31,7 @@ export class Return extends Instruction { export class Revert extends Instruction { static type: string = 'RETURN'; static readonly opcode: Opcode = Opcode.REVERT; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -58,8 +56,7 @@ export class Revert extends Instruction { export class Jump extends Instruction { static type: string = 'JUMP'; static readonly opcode: Opcode = Opcode.JUMP; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT32]; constructor(private jumpOffset: number) { @@ -102,8 +99,7 @@ export class JumpI extends Instruction { export class InternalCall extends Instruction { static readonly type: string = 'INTERNALCALL'; static readonly opcode: Opcode = Opcode.INTERNALCALL; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT32]; constructor(private loc: number) { @@ -119,8 +115,7 @@ export class InternalCall extends Instruction { export class InternalReturn extends Instruction { static readonly type: string = 'INTERNALRETURN'; static readonly opcode: Opcode = Opcode.INTERNALRETURN; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [OperandType.UINT8]; constructor() { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts index 591fcb8303f..8dc59330e94 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -6,7 +6,7 @@ import { Opcode, OperandType } from '../serialization/instruction_serialization. import { Instruction } from './instruction.js'; abstract class GetterInstruction extends Instruction { - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT32]; constructor(protected indirect: number, protected dstOffset: number) { @@ -19,7 +19,6 @@ abstract class GetterInstruction extends Instruction { this.incrementPc(machineState); } - protected abstract get opcode(): Opcode; protected abstract getIt(env: AvmExecutionEnvironment): any; } @@ -27,9 +26,6 @@ export class Address extends GetterInstruction { static type: string = 'ADDRESS'; static readonly opcode: Opcode = Opcode.ADDRESS; - protected get opcode() { - return Address.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.address; } @@ -39,9 +35,6 @@ export class StorageAddress extends GetterInstruction { static type: string = 'STORAGEADDRESS'; static readonly opcode: Opcode = Opcode.STORAGEADDRESS; - protected get opcode() { - return StorageAddress.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.storageAddress; } @@ -51,9 +44,6 @@ export class Sender extends GetterInstruction { static type: string = 'SENDER'; static readonly opcode: Opcode = Opcode.SENDER; - protected get opcode() { - return Sender.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.sender; } @@ -63,9 +53,6 @@ export class Origin extends GetterInstruction { static type: string = 'ORIGIN'; static readonly opcode: Opcode = Opcode.ORIGIN; - protected get opcode() { - return Origin.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.origin; } @@ -75,9 +62,6 @@ export class FeePerL1Gas extends GetterInstruction { static type: string = 'FEEPERL1GAS'; static readonly opcode: Opcode = Opcode.FEEPERL1GAS; - protected get opcode() { - return FeePerL1Gas.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.feePerL1Gas; } @@ -87,9 +71,6 @@ export class FeePerL2Gas extends GetterInstruction { static type: string = 'FEEPERL2GAS'; static readonly opcode: Opcode = Opcode.FEEPERL2GAS; - protected get opcode() { - return FeePerL2Gas.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.feePerL2Gas; } @@ -99,9 +80,6 @@ export class FeePerDAGas extends GetterInstruction { static type: string = 'FEEPERDAGAS'; static readonly opcode: Opcode = Opcode.FEEPERDAGAS; - protected get opcode() { - return FeePerDAGas.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.feePerDaGas; } @@ -111,9 +89,6 @@ export class Portal extends GetterInstruction { static type: string = 'PORTAL'; static readonly opcode: Opcode = Opcode.PORTAL; - protected get opcode() { - return Portal.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.portal.toField(); } @@ -123,9 +98,6 @@ export class ChainId extends GetterInstruction { static type: string = 'CHAINID'; static readonly opcode: Opcode = Opcode.CHAINID; - protected get opcode() { - return ChainId.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.globals.chainId; } @@ -135,9 +107,6 @@ export class Version extends GetterInstruction { static type: string = 'VERSION'; static readonly opcode: Opcode = Opcode.VERSION; - protected get opcode() { - return Version.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.globals.version; } @@ -147,9 +116,6 @@ export class BlockNumber extends GetterInstruction { static type: string = 'BLOCKNUMBER'; static readonly opcode: Opcode = Opcode.BLOCKNUMBER; - protected get opcode() { - return BlockNumber.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.globals.blockNumber; } @@ -159,9 +125,6 @@ export class Timestamp extends GetterInstruction { static type: string = 'TIMESTAMP'; static readonly opcode: Opcode = Opcode.TIMESTAMP; - protected get opcode() { - return Timestamp.opcode; - } protected getIt(env: AvmExecutionEnvironment): any { return env.globals.timestamp; } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts index 3caff774d1b..7d9fe5558fb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -10,8 +10,7 @@ import { Instruction } from './instruction.js'; export class Call extends Instruction { static type: string = 'CALL'; static readonly opcode: Opcode = Opcode.CALL; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -71,8 +70,7 @@ export class Call extends Instruction { export class StaticCall extends Instruction { static type: string = 'STATICCALL'; static readonly opcode: Opcode = Opcode.STATICCALL; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index d6554d72140..930061f3107 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,8 +1,10 @@ +import { assert } from 'console'; + import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmJournal } from '../journal/index.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; -import { deserialize, serialize } from '../serialization/instruction_serialization.js'; +import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; export abstract class Instruction { public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; @@ -32,7 +34,14 @@ export abstract class Instruction { } } - public static deserialize; wireFormat: any }>( + /** + * Deserializes a subclass of Instruction from a Buffer. + * If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}. + * @param this Class object to deserialize to. + * @param buf Buffer to read from. + * @returns Constructed instance of Class. + */ + public static deserialize; wireFormat: OperandType[] }>( this: T, buf: BufferCursor | Buffer, ): InstanceType { @@ -42,6 +51,7 @@ export abstract class Instruction { } public serialize(this: any): Buffer { + assert(this instanceof Instruction); return serialize(this.constructor.wireFormat, this); } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts index 2ccc4a33251..fb8b8621a71 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_impl.ts @@ -1,9 +1,12 @@ -import { BufferCursor } from '../serialization/buffer_cursor.js'; -import { Opcode, OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; +import { OperandType } from '../serialization/instruction_serialization.js'; import { Instruction } from './instruction.js'; +/** + * Covers (de)serialization for an instruction with: + * indirect, inTag, and two UINT32s. + */ export abstract class TwoOperandInstruction extends Instruction { - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -20,22 +23,14 @@ export abstract class TwoOperandInstruction extends Instruction { ) { super(); } - - protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { - const res = deserialize(buf, TwoOperandInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(TwoOperandInstruction.wireFormat, this); - } - - protected abstract get opcode(): Opcode; } +/** + * Covers (de)serialization for an instruction with: + * indirect, inTag, and three UINT32s. + */ export abstract class ThreeOperandInstruction extends Instruction { - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -54,16 +49,4 @@ export abstract class ThreeOperandInstruction extends Instruction { ) { super(); } - - protected static deserializeBase(buf: BufferCursor | Buffer): ConstructorParameters { - const res = deserialize(buf, ThreeOperandInstruction.wireFormat); - const params = res.slice(1); // Remove opcode. - return params as ConstructorParameters; - } - - public serialize(): Buffer { - return serialize(ThreeOperandInstruction.wireFormat, this); - } - - protected abstract get opcode(): Opcode; } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 927e3f5bec1..31b05c95969 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -8,8 +8,7 @@ import { TwoOperandInstruction } from './instruction_impl.js'; export class Set extends Instruction { static readonly type: string = 'SET'; static readonly opcode: Opcode = Opcode.SET; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -34,8 +33,7 @@ export class Set extends Instruction { export class CMov extends Instruction { static readonly type: string = 'CMOV'; static readonly opcode: Opcode = Opcode.CMOV; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -75,10 +73,6 @@ export class Cast extends TwoOperandInstruction { super(indirect, dstTag, aOffset, dstOffset); } - protected get opcode() { - return Cast.opcode; - } - async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const a = machineState.memory.get(this.aOffset); @@ -95,8 +89,7 @@ export class Cast extends TwoOperandInstruction { export class Mov extends Instruction { static readonly type: string = 'MOV'; static readonly opcode: Opcode = Opcode.MOV; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -120,8 +113,7 @@ export class Mov extends Instruction { export class CalldataCopy extends Instruction { static readonly type: string = 'CALLDATACOPY'; static readonly opcode: Opcode = Opcode.CALLDATACOPY; - - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts index 5bf5aae63c6..5cd9bee9ceb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -7,7 +7,7 @@ import { Opcode, OperandType } from '../serialization/instruction_serialization. import { Instruction, InstructionExecutionError } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { - // Instruction wire format with opcode. + // Informs (de)serialization. See Instruction.deserialize. public static readonly wireFormat: OperandType[] = [ OperandType.UINT8, OperandType.UINT8, @@ -18,8 +18,6 @@ abstract class BaseStorageInstruction extends Instruction { constructor(protected indirect: number, protected aOffset: number, protected bOffset: number) { super(); } - - protected abstract get opcode(): Opcode; } export class SStore extends BaseStorageInstruction { @@ -30,10 +28,6 @@ export class SStore extends BaseStorageInstruction { super(indirect, srcOffset, slotOffset); } - protected get opcode() { - return SStore.opcode; - } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { if (machineState.executionEnvironment.isStaticCall) { throw new StaticCallStorageAlterError(); @@ -60,10 +54,6 @@ export class SLoad extends BaseStorageInstruction { super(indirect, slotOffset, dstOffset); } - protected get opcode() { - return SLoad.opcode; - } - async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { const slot = machineState.memory.get(this.aOffset); diff --git a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts index ec7092766fd..b0285018980 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/bytecode_serialization.ts @@ -133,18 +133,16 @@ interface Serializable { } /** - * TODO: doc - * @param opcode - the opcode to encode - * @param args - the arguments to encode - * @returns the bytecode for this one instruction + * Serializes an array of instructions to bytecode. */ export function encodeToBytecode(instructions: Serializable[]): Buffer { return Buffer.concat(instructions.map(i => i.serialize())); } /** - * Convert a buffer of bytecode into an array of instructions - * @param bytecode - Buffer of bytecode + * Convert a buffer of bytecode into an array of instructions. + * @param bytecode Buffer of bytecode. + * @param instructionSet Optional {@code InstructionSet} to be used for deserialization. * @returns Bytecode decoded into an ordered array of Instructions */ export function decodeFromBytecode(bytecode: Buffer, instructionSet: InstructionSet = INSTRUCTION_SET): Instruction[] { diff --git a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts index 2e847f84466..0cba3caef37 100644 --- a/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/acir-simulator/src/avm/serialization/instruction_serialization.ts @@ -67,6 +67,7 @@ export enum Opcode { TOTAL_OPCODES_NUMBER, } +// Possible types for an instruction's operand in its wire format. export enum OperandType { UINT8, UINT16, @@ -75,6 +76,18 @@ export enum OperandType { UINT128, } +type OperandNativeType = number | bigint; +type OperandWriter = (value: any) => void; + +// Specifies how to read and write each operand type. +const OPERAND_SPEC = new Map OperandNativeType, OperandWriter]>([ + [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], + [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], + [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], + [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], + [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], +]); + function readBigInt128BE(this: Buffer): bigint { const totalBytes = 16; let ret: bigint = 0n; @@ -93,15 +106,13 @@ function writeBigInt128BE(this: Buffer, value: bigint): void { } } -const OPERAND_SPEC = new Map any, (value: any) => any]>([ - [OperandType.UINT8, [1, Buffer.prototype.readUint8, Buffer.prototype.writeUint8]], - [OperandType.UINT16, [2, Buffer.prototype.readUint16BE, Buffer.prototype.writeUint16BE]], - [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], - [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], - [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], -]); - -export function deserialize(cursor: BufferCursor | Buffer, operands: OperandType[]): any[] { +/** + * Reads an array of operands from a buffer. + * @param cursor Buffer to read from. Might be longer than needed. + * @param operands Specification of the operand types. + * @returns An array as big as {@code operands}, with the converted TS values. + */ +export function deserialize(cursor: BufferCursor | Buffer, operands: OperandType[]): (number | bigint)[] { const argValues = []; if (cursor instanceof Buffer) { cursor = new BufferCursor(cursor); @@ -117,15 +128,28 @@ export function deserialize(cursor: BufferCursor | Buffer, operands: OperandType return argValues; } +/** + * Serializes a class using the specified operand types. + * More specifically, this serializes {@code [cls.constructor.opcode, ...Object.values(cls)]}. + * Observe in particular that: + * (1) the first operand type specified must correspond to the opcode; + * (2) the rest of the operand types must be specified in the order returned by {@code Object.values()}. + * @param operands Type specification for the values to be serialized. + * @param cls The class to be serialized. + * @returns + */ export function serialize(operands: OperandType[], cls: any): Buffer { const chunks: Buffer[] = []; // TODO: infer opcode not in this loop - const classValues = [cls.constructor.opcode, ...Object.values(cls)]; + assert(cls.constructor.opcode !== undefined && cls.constructor.opcode !== null); + const rawClassValues: any[] = [cls.constructor.opcode, ...Object.values(cls)]; assert( - classValues.length === operands.length, - `Got ${classValues.length} values but only ${operands.length} serialization operands are specified!`, + rawClassValues.length === operands.length, + `Got ${rawClassValues.length} values but only ${operands.length} serialization operands are specified!`, ); + const classValues = rawClassValues as OperandNativeType[]; + for (let i = 0; i < operands.length; i++) { const opType = operands[i]; const [sizeBytes, _reader, writer] = OPERAND_SPEC.get(opType)!; From ba6ec23830697ea6a1c37369250c94109296a978 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 13:31:56 +0000 Subject: [PATCH 21/25] comments --- yarn-project/acir-simulator/src/avm/opcodes/index.ts | 2 +- yarn-project/acir-simulator/src/avm/opcodes/instruction.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index 56874ca5073..2fa19f7b04a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -5,7 +5,7 @@ export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; export * from './storage.js'; -// FIXME: dependency cycle +// TODO(https://github.com/AztecProtocol/aztec-packages/issues/4359): dependency cycle // export * from './external_calls.js'; export * from './environment_getters.js'; export * from './accrued_substate.js'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index 930061f3107..ff5fdd5e96f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -6,6 +6,10 @@ import { AvmJournal } from '../journal/index.js'; import { BufferCursor } from '../serialization/buffer_cursor.js'; import { OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js'; +/** + * Parent class for all AVM instructions. + * It's most important aspects are execution and (de)serialization. + */ export abstract class Instruction { public abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; From c5d9873a86ff93a6ebfe74c16ac7770461aebb67 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 14:46:16 +0000 Subject: [PATCH 22/25] more succinct tests and fix (skip) the noir contracts test --- .../acir-simulator/src/avm/index.test.ts | 3 +- .../src/avm/opcodes/arithmetic.test.ts | 216 +++--------- .../src/avm/opcodes/bitwise.test.ts | 313 ++++-------------- .../src/avm/opcodes/comparators.test.ts | 162 ++------- .../src/avm/opcodes/control_flow.test.ts | 144 ++------ .../avm/opcodes/environment_getters.test.ts | 24 +- .../src/avm/opcodes/external_calls.test.ts | 192 ++--------- .../src/avm/opcodes/memory.test.ts | 252 +++----------- .../src/avm/opcodes/storage.test.ts | 72 +--- 9 files changed, 296 insertions(+), 1082 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index aaa7dc1b874..9826a89d8e5 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -38,7 +38,8 @@ describe('avm', () => { }); describe('testing transpiled Noir contracts', () => { - it('Should execute contract function that performs addition', async () => { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/4361): sync wire format w/transpiler. + it.skip('Should execute contract function that performs addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; const journal = mock(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 0c67f4f980d..cbe268ba5cf 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -18,27 +18,12 @@ describe('Arithmetic Instructions', () => { describe('Add', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Add.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Add.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Add.deserialize(buf); @@ -63,27 +48,12 @@ describe('Arithmetic Instructions', () => { ); const expected = Buffer.from([ - // opcode - Add.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Add.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -132,27 +102,12 @@ describe('Arithmetic Instructions', () => { describe('Sub', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Sub.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Sub.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Sub.deserialize(buf); @@ -177,27 +132,12 @@ describe('Arithmetic Instructions', () => { ); const expected = Buffer.from([ - // opcode - Sub.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Sub.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -226,27 +166,12 @@ describe('Arithmetic Instructions', () => { describe('Mul', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Mul.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Mul.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Mul.deserialize(buf); @@ -271,27 +196,12 @@ describe('Arithmetic Instructions', () => { ); const expected = Buffer.from([ - // opcode - Mul.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Mul.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -340,27 +250,12 @@ describe('Arithmetic Instructions', () => { describe('Div', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Div.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Div.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Div.deserialize(buf); @@ -385,27 +280,12 @@ describe('Arithmetic Instructions', () => { ); const expected = Buffer.from([ - // opcode - Div.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Div.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 06dbcf104d5..7dcd2ff89d8 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -18,27 +18,12 @@ describe('Bitwise instructions', () => { describe('AND', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - And.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + And.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: And = And.deserialize(buf); @@ -63,27 +48,12 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - And.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + And.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -108,27 +78,12 @@ describe('Bitwise instructions', () => { describe('OR', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Or.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Or.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Or = Or.deserialize(buf); @@ -153,27 +108,12 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - Or.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Or.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -202,27 +142,12 @@ describe('Bitwise instructions', () => { describe('XOR', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Xor.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Xor.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Xor = Xor.deserialize(buf); @@ -247,27 +172,12 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - Xor.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Xor.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -296,27 +206,12 @@ describe('Bitwise instructions', () => { describe('SHR', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Shr.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Shr.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Shr = Shr.deserialize(buf); @@ -341,27 +236,12 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - Shr.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Shr.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -430,27 +310,12 @@ describe('Bitwise instructions', () => { describe('SHL', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Shl.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Shl.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Shl = Shl.deserialize(buf); @@ -475,27 +340,12 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - Shl.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Shl.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -584,24 +434,12 @@ describe('Bitwise instructions', () => { describe('NOT', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Not.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Not.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - const inst: Not = Not.deserialize(buf); expect(inst).toEqual( new Not(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*aOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a), @@ -617,22 +455,11 @@ describe('Bitwise instructions', () => { ); const expected = Buffer.from([ - // opcode - Not.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Not.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index bda41c65e60..12b2e9de151 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -19,27 +19,12 @@ describe('Comparators', () => { describe('Eq', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Eq.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Eq.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Eq = Eq.deserialize(buf); @@ -64,27 +49,12 @@ describe('Comparators', () => { ); const expected = Buffer.from([ - // opcode - Eq.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Eq.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -134,27 +104,12 @@ describe('Comparators', () => { describe('Lt', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Lt.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Lt.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Lt = Lt.deserialize(buf); @@ -179,27 +134,12 @@ describe('Comparators', () => { ); const expected = Buffer.from([ - // opcode - Lt.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Lt.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -249,27 +189,12 @@ describe('Comparators', () => { describe('Lte', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Lte.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Lte.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst: Lte = Lte.deserialize(buf); @@ -294,27 +219,12 @@ describe('Comparators', () => { ); const expected = Buffer.from([ - // opcode - Lte.opcode, - // indirect - 0x01, - // inTag - TypeTag.UINT64, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Lte.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); 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 index c1f87a41f99..b312bd2e467 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -21,13 +21,8 @@ describe('Control Flow Opcodes', () => { describe('JUMP', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Jump.opcode, - // loc - 0x12, - 0x34, - 0x56, - 0x78, + Jump.opcode, // opcode + ...Buffer.from('12345678', 'hex'), // loc ]); const inst: Jump = Jump.deserialize(buf); @@ -38,13 +33,8 @@ describe('Control Flow Opcodes', () => { const inst = new Jump(/*loc=*/ 0x12345678); const expected = Buffer.from([ - // opcode - Jump.opcode, - // loc - 0x12, - 0x34, - 0x56, - 0x78, + Jump.opcode, // opcode + ...Buffer.from('12345678', 'hex'), // loc ]); expect(inst.serialize()).toEqual(expected); }); @@ -63,20 +53,10 @@ describe('Control Flow Opcodes', () => { describe('JUMPI', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - JumpI.opcode, - // indirect - 0x01, - // loc - 0x12, - 0x34, - 0x56, - 0x78, - // condOffset - 0xa2, - 0x34, - 0x56, - 0x78, + JumpI.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // loc + ...Buffer.from('a2345678', 'hex'), // condOffset ]); const inst: JumpI = JumpI.deserialize(buf); @@ -87,20 +67,10 @@ describe('Control Flow Opcodes', () => { const inst = new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678); const expected = Buffer.from([ - // opcode - JumpI.opcode, - // indirect - 0x01, - // loc - 0x12, - 0x34, - 0x56, - 0x78, - // condOffset - 0xa2, - 0x34, - 0x56, - 0x78, + JumpI.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // loc + ...Buffer.from('a2345678', 'hex'), // condOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -140,13 +110,8 @@ describe('Control Flow Opcodes', () => { describe('INTERNALCALL and RETURN', () => { it('INTERNALCALL should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - InternalCall.opcode, - // loc - 0x12, - 0x34, - 0x56, - 0x78, + InternalCall.opcode, // opcode + ...Buffer.from('12345678', 'hex'), // loc ]); const inst = InternalCall.deserialize(buf); @@ -157,13 +122,8 @@ describe('Control Flow Opcodes', () => { const inst = new InternalCall(/*loc=*/ 0x12345678); const expected = Buffer.from([ - // opcode - InternalCall.opcode, - // loc - 0x12, - 0x34, - 0x56, - 0x78, + InternalCall.opcode, // opcode + ...Buffer.from('12345678', 'hex'), // loc ]); expect(inst.serialize()).toEqual(expected); @@ -230,20 +190,10 @@ describe('Control Flow Opcodes', () => { describe('RETURN', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Return.opcode, - // indirect - 0x01, - // returnOffset - 0x12, - 0x34, - 0x56, - 0x78, - // copySize - 0xa2, - 0x34, - 0x56, - 0x78, + Return.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // returnOffset + ...Buffer.from('a2345678', 'hex'), // copySize ]); const inst = Return.deserialize(buf); @@ -254,20 +204,10 @@ describe('Control Flow Opcodes', () => { const inst = new Return(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*copySize=*/ 0xa2345678); const expected = Buffer.from([ - // opcode - Return.opcode, - // indirect - 0x01, - // returnOffset - 0x12, - 0x34, - 0x56, - 0x78, - // copySize - 0xa2, - 0x34, - 0x56, - 0x78, + Return.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // returnOffset + ...Buffer.from('a2345678', 'hex'), // copySize ]); expect(inst.serialize()).toEqual(expected); @@ -292,20 +232,10 @@ describe('Control Flow Opcodes', () => { describe('REVERT', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Revert.opcode, - // indirect - 0x01, - // returnOffset - 0x12, - 0x34, - 0x56, - 0x78, - // retSize - 0xa2, - 0x34, - 0x56, - 0x78, + Revert.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // returnOffset + ...Buffer.from('a2345678', 'hex'), // retSize ]); const inst = Revert.deserialize(buf); @@ -316,20 +246,10 @@ describe('Control Flow Opcodes', () => { const inst = new Revert(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*retSize=*/ 0xa2345678); const expected = Buffer.from([ - // opcode - Revert.opcode, - // indirect - 0x01, - // returnOffset - 0x12, - 0x34, - 0x56, - 0x78, - // retSize - 0xa2, - 0x34, - 0x56, - 0x78, + Revert.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // returnOffset + ...Buffer.from('a2345678', 'hex'), // retSize ]); expect(inst.serialize()).toEqual(expected); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 4198b8e4850..40fd3254687 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -40,15 +40,9 @@ describe('Environment getters instructions', () => { describe('ADDRESS', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Address.opcode, - // indirect - 0x01, - // dstOffset - 0x12, - 0x34, - 0x56, - 0x78, + Address.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset ]); const inst = Address.deserialize(buf); @@ -59,15 +53,9 @@ describe('Environment getters instructions', () => { const inst = new Address(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); const expected = Buffer.from([ - // opcode - Address.opcode, - // indirect - 0x01, - // dstOffset - 0x12, - 0x34, - 0x56, - 0x78, + Address.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index d7ac48f3d43..7d29ec9af9d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -36,45 +36,15 @@ describe('External Calls', () => { describe('Call', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Call.opcode, - // indirect - 0x01, - // gasOffset - 0x12, - 0x34, - 0x56, - 0x78, - // addrOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // argsOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // argsSize - 0xc2, - 0x34, - 0x56, - 0x78, - // retOffset - 0xd2, - 0x34, - 0x56, - 0x78, - // retSize - 0xe2, - 0x34, - 0x56, - 0x78, - // successOffset - 0xf2, - 0x34, - 0x56, - 0x78, + Call.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // gasOffset + ...Buffer.from('a2345678', 'hex'), // addrOffset + ...Buffer.from('b2345678', 'hex'), // argsOffset + ...Buffer.from('c2345678', 'hex'), // argsSize + ...Buffer.from('d2345678', 'hex'), // retOffset + ...Buffer.from('e2345678', 'hex'), // retSize + ...Buffer.from('f2345678', 'hex'), // successOffset ]); const inst = Call.deserialize(buf); @@ -105,45 +75,15 @@ describe('External Calls', () => { ); const expected = Buffer.from([ - // opcode - Call.opcode, - // indirect - 0x01, - // gasOffset - 0x12, - 0x34, - 0x56, - 0x78, - // addrOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // argsOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // argsSize - 0xc2, - 0x34, - 0x56, - 0x78, - // retOffset - 0xd2, - 0x34, - 0x56, - 0x78, - // retSize - 0xe2, - 0x34, - 0x56, - 0x78, - // successOffset - 0xf2, - 0x34, - 0x56, - 0x78, + Call.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // gasOffset + ...Buffer.from('a2345678', 'hex'), // addrOffset + ...Buffer.from('b2345678', 'hex'), // argsOffset + ...Buffer.from('c2345678', 'hex'), // argsSize + ...Buffer.from('d2345678', 'hex'), // retOffset + ...Buffer.from('e2345678', 'hex'), // retSize + ...Buffer.from('f2345678', 'hex'), // successOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -207,45 +147,15 @@ describe('External Calls', () => { describe('Static Call', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - StaticCall.opcode, - // indirect - 0x01, - // gasOffset - 0x12, - 0x34, - 0x56, - 0x78, - // addrOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // argsOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // argsSize - 0xc2, - 0x34, - 0x56, - 0x78, - // retOffset - 0xd2, - 0x34, - 0x56, - 0x78, - // retSize - 0xe2, - 0x34, - 0x56, - 0x78, - // successOffset - 0xf2, - 0x34, - 0x56, - 0x78, + StaticCall.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // gasOffset + ...Buffer.from('a2345678', 'hex'), // addrOffset + ...Buffer.from('b2345678', 'hex'), // argsOffset + ...Buffer.from('c2345678', 'hex'), // argsSize + ...Buffer.from('d2345678', 'hex'), // retOffset + ...Buffer.from('e2345678', 'hex'), // retSize + ...Buffer.from('f2345678', 'hex'), // successOffset ]); const inst = StaticCall.deserialize(buf); @@ -276,45 +186,15 @@ describe('External Calls', () => { ); const expected = Buffer.from([ - // opcode - StaticCall.opcode, - // indirect - 0x01, - // gasOffset - 0x12, - 0x34, - 0x56, - 0x78, - // addrOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // argsOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // argsSize - 0xc2, - 0x34, - 0x56, - 0x78, - // retOffset - 0xd2, - 0x34, - 0x56, - 0x78, - // retSize - 0xe2, - 0x34, - 0x56, - 0x78, - // successOffset - 0xf2, - 0x34, - 0x56, - 0x78, + StaticCall.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // gasOffset + ...Buffer.from('a2345678', 'hex'), // addrOffset + ...Buffer.from('b2345678', 'hex'), // argsOffset + ...Buffer.from('c2345678', 'hex'), // argsSize + ...Buffer.from('d2345678', 'hex'), // retOffset + ...Buffer.from('e2345678', 'hex'), // retSize + ...Buffer.from('f2345678', 'hex'), // successOffset ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 6f7b5744645..493f025811b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -20,34 +20,11 @@ describe('Memory instructions', () => { describe('SET', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Set.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // const (will be 128 bit) - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Set.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678123456781234567812345678', 'hex'), // const (will be 128 bit) + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Set.deserialize(buf); @@ -70,34 +47,11 @@ describe('Memory instructions', () => { ); const expected = Buffer.from([ - // opcode - Set.opcode, - // indirect - 0x01, - // inTag - TypeTag.FIELD, - // const (will be 128 bit) - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Set.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // inTag + ...Buffer.from('12345678123456781234567812345678', 'hex'), // const (will be 128 bit) + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -134,22 +88,11 @@ describe('Memory instructions', () => { describe('CAST', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Cast.opcode, - // indirect - 0x01, - // dstTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Cast.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // dstTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Cast.deserialize(buf); @@ -167,22 +110,11 @@ describe('Memory instructions', () => { ); const expected = Buffer.from([ - // opcode - Cast.opcode, - // indirect - 0x01, - // dstTag - TypeTag.FIELD, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Cast.opcode, // opcode + 0x01, // indirect + TypeTag.FIELD, // dstTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -313,20 +245,10 @@ describe('Memory instructions', () => { describe('MOV', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - Mov.opcode, - // indirect - 0x01, - // srcOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Mov.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // srcOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = Mov.deserialize(buf); @@ -337,20 +259,10 @@ describe('Memory instructions', () => { const inst = new Mov(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a); const expected = Buffer.from([ - // opcode - Mov.opcode, - // indirect - 0x01, - // srcOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + Mov.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // srcOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -381,30 +293,12 @@ describe('Memory instructions', () => { describe('CMOV', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - CMov.opcode, - // indirect - 0x01, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // condOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + CMov.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('a2345678', 'hex'), // bOffset + ...Buffer.from('b2345678', 'hex'), // condOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = CMov.deserialize(buf); @@ -429,30 +323,12 @@ describe('Memory instructions', () => { ); const expected = Buffer.from([ - // opcode - CMov.opcode, - // indirect - 0x01, - // aOffset - 0x12, - 0x34, - 0x56, - 0x78, - // bOffset - 0xa2, - 0x34, - 0x56, - 0x78, - // condOffset - 0xb2, - 0x34, - 0x56, - 0x78, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + CMov.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('a2345678', 'hex'), // bOffset + ...Buffer.from('b2345678', 'hex'), // condOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -525,25 +401,11 @@ describe('Memory instructions', () => { describe('CALLDATACOPY', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - CalldataCopy.opcode, - // indirect - 0x01, - // cdOffset - 0x12, - 0x34, - 0x56, - 0x78, - // copysize - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + CalldataCopy.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // cdOffset + ...Buffer.from('23456789', 'hex'), // copysize + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); const inst = CalldataCopy.deserialize(buf); @@ -566,25 +428,11 @@ describe('Memory instructions', () => { ); const expected = Buffer.from([ - // opcode - CalldataCopy.opcode, - // indirect - 0x01, - // cdOffset - 0x12, - 0x34, - 0x56, - 0x78, - // copysize - 0x23, - 0x45, - 0x67, - 0x89, - // dstOffset - 0x34, - 0x56, - 0x78, - 0x9a, + CalldataCopy.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // cdOffset + ...Buffer.from('23456789', 'hex'), // copysize + ...Buffer.from('3456789a', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index dbfd4bf1296..07c432a9a7c 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -24,20 +24,10 @@ describe('Storage Instructions', () => { describe('SSTORE', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - SStore.opcode, - // indirect - 0x01, - // srcOffset - 0x12, - 0x34, - 0x56, - 0x78, - // slotOffset - 0xa2, - 0x34, - 0x56, - 0x78, + SStore.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // srcOffset + ...Buffer.from('a2345678', 'hex'), // slotOffset ]); const inst = SStore.deserialize(buf); @@ -48,20 +38,10 @@ describe('Storage Instructions', () => { const inst = new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678); const expected = Buffer.from([ - // opcode - SStore.opcode, - // indirect - 0x01, - // srcOffset - 0x12, - 0x34, - 0x56, - 0x78, - // slotOffset - 0xa2, - 0x34, - 0x56, - 0x78, + SStore.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // srcOffset + ...Buffer.from('a2345678', 'hex'), // slotOffset ]); expect(inst.serialize()).toEqual(expected); }); @@ -97,20 +77,10 @@ describe('Storage Instructions', () => { describe('SLOAD', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - // opcode - SLoad.opcode, - // indirect - 0x01, - // slotOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0xa2, - 0x34, - 0x56, - 0x78, + SLoad.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // slotOffset + ...Buffer.from('a2345678', 'hex'), // dstOffset ]); const inst = SLoad.deserialize(buf); @@ -121,20 +91,10 @@ describe('Storage Instructions', () => { const inst = new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678); const expected = Buffer.from([ - // opcode - SLoad.opcode, - // indirect - 0x01, - // slotOffset - 0x12, - 0x34, - 0x56, - 0x78, - // dstOffset - 0xa2, - 0x34, - 0x56, - 0x78, + SLoad.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // slotOffset + ...Buffer.from('a2345678', 'hex'), // dstOffset ]); expect(inst.serialize()).toEqual(expected); }); From 027df8ebff4bf67da556b94d56cb11fbef6a99ab Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 14:56:56 +0000 Subject: [PATCH 23/25] serialization tests for env getters --- .../avm/opcodes/environment_getters.test.ts | 332 ++++++++++++++++-- 1 file changed, 298 insertions(+), 34 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 40fd3254687..7788ffa5086 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -37,7 +37,7 @@ describe('Environment getters instructions', () => { expect(actual).toEqual(value); }; - describe('ADDRESS', () => { + describe('Address', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ Address.opcode, // opcode @@ -66,39 +66,207 @@ describe('Environment getters instructions', () => { }); }); - it('Should read storage address correctly', async () => { - const address = new Fr(123456n); - await envGetterTest('storageAddress', address, new StorageAddress(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('StorageAddress', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + StorageAddress.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = StorageAddress.deserialize(buf); + expect(inst).toEqual(new StorageAddress(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new StorageAddress(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + StorageAddress.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read storage address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('storageAddress', address, new StorageAddress(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read Portal correctly', async () => { - const portal = new Fr(123456n); - await envGetterTest('portal', portal, new Portal(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('Portal', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Portal.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = Portal.deserialize(buf); + expect(inst).toEqual(new Portal(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Portal(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + Portal.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read Portal correctly', async () => { + const portal = new Fr(123456n); + await envGetterTest('portal', portal, new Portal(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read FeePerL1Gas correctly', async () => { - const feePerL1Gas = new Fr(123456n); - await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('FeePerL1Gas', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + FeePerL1Gas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = FeePerL1Gas.deserialize(buf); + expect(inst).toEqual(new FeePerL1Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new FeePerL1Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + FeePerL1Gas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read FeePerL1Gas correctly', async () => { + const feePerL1Gas = new Fr(123456n); + await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read FeePerL2Gas correctly', async () => { - const feePerL2Gas = new Fr(123456n); - await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('FeePerL2Gas', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + FeePerL2Gas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = FeePerL2Gas.deserialize(buf); + expect(inst).toEqual(new FeePerL2Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new FeePerL2Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + FeePerL2Gas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read FeePerL2Gas correctly', async () => { + const feePerL2Gas = new Fr(123456n); + await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read FeePerDAGas correctly', async () => { - const feePerDaGas = new Fr(123456n); - await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('FeePerDAGas', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + FeePerDAGas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = FeePerDAGas.deserialize(buf); + expect(inst).toEqual(new FeePerDAGas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new FeePerDAGas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + FeePerDAGas.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read FeePerDAGas correctly', async () => { + const feePerDaGas = new Fr(123456n); + await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read Origin correctly', async () => { - const origin = new Fr(123456n); - await envGetterTest('origin', origin, new Origin(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('Origin', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Origin.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = Origin.deserialize(buf); + expect(inst).toEqual(new Origin(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Origin(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + Origin.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read Origin correctly', async () => { + const origin = new Fr(123456n); + await envGetterTest('origin', origin, new Origin(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read Sender correctly', async () => { - const sender = new Fr(123456n); - await envGetterTest('sender', sender, new Sender(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('Sender', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Sender.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = Sender.deserialize(buf); + expect(inst).toEqual(new Sender(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Sender(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + Sender.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read Sender correctly', async () => { + const sender = new Fr(123456n); + await envGetterTest('sender', sender, new Sender(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); describe('Global Variables', () => { @@ -112,24 +280,120 @@ describe('Environment getters instructions', () => { expect(actual).toEqual(value); }; - it('Should read chainId', async () => { - const chainId = new Fr(123456n); - await readGlobalVariableTest('chainId', chainId, new ChainId(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('chainId', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + ChainId.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = ChainId.deserialize(buf); + expect(inst).toEqual(new ChainId(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new ChainId(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + ChainId.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read chainId', async () => { + const chainId = new Fr(123456n); + await readGlobalVariableTest('chainId', chainId, new ChainId(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read version', async () => { - const version = new Fr(123456n); - await readGlobalVariableTest('version', version, new Version(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('version', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Version.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = Version.deserialize(buf); + expect(inst).toEqual(new Version(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Version(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + Version.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read version', async () => { + const version = new Fr(123456n); + await readGlobalVariableTest('version', version, new Version(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read block number', async () => { - const blockNumber = new Fr(123456n); - await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('block', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + BlockNumber.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = BlockNumber.deserialize(buf); + expect(inst).toEqual(new BlockNumber(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new BlockNumber(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + BlockNumber.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read block number', async () => { + const blockNumber = new Fr(123456n); + await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); - it('Should read timestamp', async () => { - const timestamp = new Fr(123456n); - await readGlobalVariableTest('timestamp', timestamp, new Timestamp(/*indirect=*/ 0, /*dstOffset=*/ 0)); + describe('timestamp', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Timestamp.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = Timestamp.deserialize(buf); + expect(inst).toEqual(new Timestamp(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new Timestamp(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + Timestamp.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should read timestamp', async () => { + const timestamp = new Fr(123456n); + await readGlobalVariableTest('timestamp', timestamp, new Timestamp(/*indirect=*/ 0, /*dstOffset=*/ 0)); + }); }); }); }); From 24be38e5cfc03fd3a68a2890b73ab6588eeb45b8 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 15:06:15 +0000 Subject: [PATCH 24/25] accrued substate: serialization tests --- .../src/avm/opcodes/accrued_substate.test.ts | 203 +++++++++++++----- 1 file changed, 153 insertions(+), 50 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index 49c8c58e001..087d9062680 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -18,56 +18,160 @@ describe('Accrued Substate', () => { machineState = new AvmMachineState(initExecutionEnvironment()); }); - it('Should append a new note hash correctly', async () => { - const value = new Field(69n); - machineState.memory.set(0, value); - - await new EmitNoteHash(/*indirect=*/ 0, 0).execute(machineState, journal); - - const journalState = journal.flush(); - const expected = [value.toFr()]; - expect(journalState.newNoteHashes).toEqual(expected); + describe('EmitNoteHash', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + EmitNoteHash.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = EmitNoteHash.deserialize(buf); + expect(inst).toEqual(new EmitNoteHash(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new EmitNoteHash(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + EmitNoteHash.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should append a new note hash correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNoteHashes).toEqual(expected); + }); }); - it('Should append a new nullifier correctly', async () => { - const value = new Field(69n); - machineState.memory.set(0, value); - - await new EmitNullifier(/*indirect=*/ 0, 0).execute(machineState, journal); - - const journalState = journal.flush(); - const expected = [value.toFr()]; - expect(journalState.newNullifiers).toEqual(expected); + describe('EmitNullifier', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + EmitNullifier.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + + const inst = EmitNullifier.deserialize(buf); + expect(inst).toEqual(new EmitNullifier(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); + }); + + it('Should serialize correctly', () => { + const inst = new EmitNullifier(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); + + const expected = Buffer.from([ + EmitNullifier.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should append a new nullifier correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNullifier(/*indirect=*/ 0, /*offset=*/ 0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNullifiers).toEqual(expected); + }); }); - it('Should append unencrypted logs correctly', async () => { - const startOffset = 0; - - const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; - machineState.memory.setSlice(0, values); - - const length = values.length; - - await new EmitUnencryptedLog(/*indirect=*/ 0, startOffset, length).execute(machineState, journal); - - const journalState = journal.flush(); - const expected = values.map(v => v.toFr()); - expect(journalState.newLogs).toEqual([expected]); + describe('EmitUnencryptedLog', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + EmitUnencryptedLog.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // offset + ...Buffer.from('a2345678', 'hex'), // length + ]); + + const inst = EmitUnencryptedLog.deserialize(buf); + expect(inst).toEqual( + new EmitUnencryptedLog(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678), + ); + }); + + it('Should serialize correctly', () => { + const inst = new EmitUnencryptedLog(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678); + + const expected = Buffer.from([ + EmitUnencryptedLog.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ...Buffer.from('a2345678', 'hex'), // length + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should append unencrypted logs correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new EmitUnencryptedLog(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); }); - it('Should append l1 to l2 messages correctly', async () => { - const startOffset = 0; - - const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; - machineState.memory.setSlice(0, values); - - const length = values.length; - - await new SendL2ToL1Message(/*indirect=*/ 0, startOffset, length).execute(machineState, journal); - - const journalState = journal.flush(); - const expected = values.map(v => v.toFr()); - expect(journalState.newLogs).toEqual([expected]); + describe('SendL2ToL1Message', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + SendL2ToL1Message.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // offset + ...Buffer.from('a2345678', 'hex'), // length + ]); + + const inst = SendL2ToL1Message.deserialize(buf); + expect(inst).toEqual( + new SendL2ToL1Message(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678), + ); + }); + + it('Should serialize correctly', () => { + const inst = new SendL2ToL1Message(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678); + + const expected = Buffer.from([ + SendL2ToL1Message.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // dstOffset + ...Buffer.from('a2345678', 'hex'), // length + ]); + expect(inst.serialize()).toEqual(expected); + }); + + it('Should append l1 to l2 messages correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new SendL2ToL1Message(/*indirect=*/ 0, /*offset=*/ startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); }); it('All substate instructions should fail within a static call', async () => { @@ -75,15 +179,14 @@ describe('Accrued Substate', () => { machineState = new AvmMachineState(executionEnvironment); const instructions = [ - new EmitNoteHash(/*indirect=*/ 0, 0), - new EmitNullifier(/*indirect=*/ 0, 0), - new EmitUnencryptedLog(/*indirect=*/ 0, 0, 1), - new SendL2ToL1Message(/*indirect=*/ 0, 0, 1), + new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ 0), + new EmitNullifier(/*indirect=*/ 0, /*offset=*/ 0), + new EmitUnencryptedLog(/*indirect=*/ 0, /*offset=*/ 0, 1), + new SendL2ToL1Message(/*indirect=*/ 0, /*offset=*/ 0, 1), ]; for (const instruction of instructions) { - const inst = () => instruction.execute(machineState, journal); - await expect(inst()).rejects.toThrow(StaticCallStorageAlterError); + await expect(instruction.execute(machineState, journal)).rejects.toThrow(StaticCallStorageAlterError); } }); }); From 044e413d9038da39d14f17344faed49f2f3e6e02 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 1 Feb 2024 16:07:29 +0000 Subject: [PATCH 25/25] squash tests --- .../src/avm/opcodes/accrued_substate.test.ts | 70 ++----- .../src/avm/opcodes/arithmetic.test.ts | 108 ++-------- .../src/avm/opcodes/bitwise.test.ts | 144 ++----------- .../src/avm/opcodes/comparators.test.ts | 75 +------ .../src/avm/opcodes/control_flow.test.ts | 84 ++------ .../avm/opcodes/environment_getters.test.ts | 192 ++++-------------- .../src/avm/opcodes/external_calls.test.ts | 66 +----- .../src/avm/opcodes/memory.test.ts | 110 ++-------- .../src/avm/opcodes/storage.test.ts | 34 +--- 9 files changed, 119 insertions(+), 764 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts index 087d9062680..72ff0a7c614 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -19,26 +19,16 @@ describe('Accrued Substate', () => { }); describe('EmitNoteHash', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ EmitNoteHash.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = EmitNoteHash.deserialize(buf); - expect(inst).toEqual(new EmitNoteHash(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new EmitNoteHash(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - EmitNoteHash.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(EmitNoteHash.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should append a new note hash correctly', async () => { @@ -54,26 +44,16 @@ describe('Accrued Substate', () => { }); describe('EmitNullifier', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ EmitNullifier.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = EmitNullifier.deserialize(buf); - expect(inst).toEqual(new EmitNullifier(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new EmitNullifier(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - EmitNullifier.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(EmitNullifier.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should append a new nullifier correctly', async () => { @@ -89,30 +69,17 @@ describe('Accrued Substate', () => { }); describe('EmitUnencryptedLog', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ EmitUnencryptedLog.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // offset ...Buffer.from('a2345678', 'hex'), // length ]); - - const inst = EmitUnencryptedLog.deserialize(buf); - expect(inst).toEqual( - new EmitUnencryptedLog(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678), - ); - }); - - it('Should serialize correctly', () => { const inst = new EmitUnencryptedLog(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678); - const expected = Buffer.from([ - EmitUnencryptedLog.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ...Buffer.from('a2345678', 'hex'), // length - ]); - expect(inst.serialize()).toEqual(expected); + expect(EmitUnencryptedLog.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should append unencrypted logs correctly', async () => { @@ -132,30 +99,17 @@ describe('Accrued Substate', () => { }); describe('SendL2ToL1Message', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ SendL2ToL1Message.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // offset ...Buffer.from('a2345678', 'hex'), // length ]); - - const inst = SendL2ToL1Message.deserialize(buf); - expect(inst).toEqual( - new SendL2ToL1Message(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678), - ); - }); - - it('Should serialize correctly', () => { const inst = new SendL2ToL1Message(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678, /*length=*/ 0xa2345678); - const expected = Buffer.from([ - SendL2ToL1Message.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ...Buffer.from('a2345678', 'hex'), // length - ]); - expect(inst.serialize()).toEqual(expected); + expect(SendL2ToL1Message.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should append l1 to l2 messages correctly', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index cbe268ba5cf..f54db2d82a7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -16,7 +16,7 @@ describe('Arithmetic Instructions', () => { }); describe('Add', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Add.opcode, // opcode 0x01, // indirect @@ -25,20 +25,6 @@ describe('Arithmetic Instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Add.deserialize(buf); - expect(inst).toEqual( - new Add( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.FIELD, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Add( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.FIELD, @@ -47,15 +33,8 @@ describe('Arithmetic Instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Add.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Add.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should add correctly over field elements', async () => { @@ -100,7 +79,7 @@ describe('Arithmetic Instructions', () => { }); describe('Sub', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Sub.opcode, // opcode 0x01, // indirect @@ -109,20 +88,6 @@ describe('Arithmetic Instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Sub.deserialize(buf); - expect(inst).toEqual( - new Sub( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.FIELD, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Sub( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.FIELD, @@ -131,15 +96,8 @@ describe('Arithmetic Instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Sub.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Sub.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should subtract correctly over field elements', async () => { @@ -164,7 +122,7 @@ describe('Arithmetic Instructions', () => { }); describe('Mul', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Mul.opcode, // opcode 0x01, // indirect @@ -173,20 +131,6 @@ describe('Arithmetic Instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Mul.deserialize(buf); - expect(inst).toEqual( - new Mul( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.FIELD, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Mul( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.FIELD, @@ -195,15 +139,8 @@ describe('Arithmetic Instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Mul.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Mul.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should multiply correctly over field elements', async () => { @@ -248,7 +185,7 @@ describe('Arithmetic Instructions', () => { }); describe('Div', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Div.opcode, // opcode 0x01, // indirect @@ -257,20 +194,6 @@ describe('Arithmetic Instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Div.deserialize(buf); - expect(inst).toEqual( - new Add( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.FIELD, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Div( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.FIELD, @@ -279,15 +202,8 @@ describe('Arithmetic Instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Div.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Div.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should perform field division', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 7dcd2ff89d8..d41c34611e0 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -25,20 +25,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: And = And.deserialize(buf); - expect(inst).toEqual( - new And( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new And( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -47,15 +33,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - And.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(And.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should AND correctly over integral types', async () => { @@ -85,20 +64,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Or = Or.deserialize(buf); - expect(inst).toEqual( - new Or( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Or( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -107,15 +72,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Or.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Or.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should OR correctly over integral types', async () => { @@ -149,20 +107,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Xor = Xor.deserialize(buf); - expect(inst).toEqual( - new Xor( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Xor( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -171,15 +115,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Xor.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Xor.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should XOR correctly over integral types', async () => { @@ -213,20 +150,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Shr = Shr.deserialize(buf); - expect(inst).toEqual( - new Shr( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Shr( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -235,15 +158,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Shr.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Shr.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should shift correctly 0 positions over integral types', async () => { @@ -317,20 +233,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Shl = Shl.deserialize(buf); - expect(inst).toEqual( - new Shl( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Shl( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -339,15 +241,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Shl.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Shl.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should shift correctly 0 positions over integral types', async () => { @@ -432,7 +327,7 @@ describe('Bitwise instructions', () => { }); describe('NOT', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Not.opcode, // opcode 0x01, // indirect @@ -440,13 +335,6 @@ describe('Bitwise instructions', () => { ...Buffer.from('12345678', 'hex'), // aOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - const inst: Not = Not.deserialize(buf); - expect(inst).toEqual( - new Not(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*aOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a), - ); - }); - - it('Should serialize correctly', () => { const inst = new Not( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -454,14 +342,8 @@ describe('Bitwise instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Not.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Not.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should NOT correctly over integral types', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts index 12b2e9de151..b707b138aa7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -26,20 +26,6 @@ describe('Comparators', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Eq = Eq.deserialize(buf); - expect(inst).toEqual( - new Eq( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Eq( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -48,15 +34,8 @@ describe('Comparators', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Eq.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Eq.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Works on integral types', async () => { @@ -111,20 +90,6 @@ describe('Comparators', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Lt = Lt.deserialize(buf); - expect(inst).toEqual( - new Lt( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Lt( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -133,15 +98,8 @@ describe('Comparators', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Lt.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Lt.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Works on integral types', async () => { @@ -196,20 +154,6 @@ describe('Comparators', () => { ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst: Lte = Lte.deserialize(buf); - expect(inst).toEqual( - new Lte( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Lte( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, @@ -218,15 +162,8 @@ describe('Comparators', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Lte.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Lte.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Works on integral types', async () => { 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 index b312bd2e467..82879c619b9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -19,24 +19,15 @@ describe('Control Flow Opcodes', () => { }); describe('JUMP', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Jump.opcode, // opcode ...Buffer.from('12345678', 'hex'), // loc ]); - - const inst: Jump = Jump.deserialize(buf); - expect(inst).toEqual(new Jump(/*loc=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Jump(/*loc=*/ 0x12345678); - const expected = Buffer.from([ - Jump.opcode, // opcode - ...Buffer.from('12345678', 'hex'), // loc - ]); - expect(inst.serialize()).toEqual(expected); + expect(Jump.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should implement JUMP', async () => { @@ -51,28 +42,17 @@ describe('Control Flow Opcodes', () => { }); describe('JUMPI', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ JumpI.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // loc ...Buffer.from('a2345678', 'hex'), // condOffset ]); - - const inst: JumpI = JumpI.deserialize(buf); - expect(inst).toEqual(new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678)); - }); - - it('Should serialize correctly', () => { const inst = new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678); - const expected = Buffer.from([ - JumpI.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // loc - ...Buffer.from('a2345678', 'hex'), // condOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(JumpI.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should implement JUMPI - truthy', async () => { @@ -108,25 +88,15 @@ describe('Control Flow Opcodes', () => { }); describe('INTERNALCALL and RETURN', () => { - it('INTERNALCALL should deserialize correctly', () => { + it('INTERNALCALL Should (de)serialize correctly', () => { const buf = Buffer.from([ InternalCall.opcode, // opcode ...Buffer.from('12345678', 'hex'), // loc ]); - - const inst = InternalCall.deserialize(buf); - expect(inst).toEqual(new InternalCall(/*loc=*/ 0x12345678)); - }); - - it('INTERNALCALL should serialize correctly', () => { const inst = new InternalCall(/*loc=*/ 0x12345678); - const expected = Buffer.from([ - InternalCall.opcode, // opcode - ...Buffer.from('12345678', 'hex'), // loc - ]); - - expect(inst.serialize()).toEqual(expected); + expect(InternalCall.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should implement Internal Call and Return', async () => { @@ -188,29 +158,17 @@ describe('Control Flow Opcodes', () => { }); describe('RETURN', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Return.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // returnOffset ...Buffer.from('a2345678', 'hex'), // copySize ]); - - const inst = Return.deserialize(buf); - expect(inst).toEqual(new Return(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*copySize=*/ 0xa2345678)); - }); - - it('Should serialize correctly', () => { const inst = new Return(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*copySize=*/ 0xa2345678); - const expected = Buffer.from([ - Return.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // returnOffset - ...Buffer.from('a2345678', 'hex'), // copySize - ]); - - expect(inst.serialize()).toEqual(expected); + expect(Return.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should return data from the return opcode', async () => { @@ -230,29 +188,17 @@ describe('Control Flow Opcodes', () => { }); describe('REVERT', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Revert.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // returnOffset ...Buffer.from('a2345678', 'hex'), // retSize ]); - - const inst = Revert.deserialize(buf); - expect(inst).toEqual(new Revert(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*retSize=*/ 0xa2345678)); - }); - - it('Should serialize correctly', () => { const inst = new Revert(/*indirect=*/ 0x01, /*returnOffset=*/ 0x12345678, /*retSize=*/ 0xa2345678); - const expected = Buffer.from([ - Revert.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // returnOffset - ...Buffer.from('a2345678', 'hex'), // retSize - ]); - - expect(inst.serialize()).toEqual(expected); + expect(Revert.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should return data and revert from the revert opcode', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts index 7788ffa5086..2acf4729546 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -38,26 +38,16 @@ describe('Environment getters instructions', () => { }; describe('Address', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Address.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Address.deserialize(buf); - expect(inst).toEqual(new Address(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Address(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Address.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Address.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read address correctly', async () => { @@ -67,26 +57,16 @@ describe('Environment getters instructions', () => { }); describe('StorageAddress', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ StorageAddress.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = StorageAddress.deserialize(buf); - expect(inst).toEqual(new StorageAddress(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new StorageAddress(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - StorageAddress.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(StorageAddress.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read storage address correctly', async () => { @@ -96,26 +76,16 @@ describe('Environment getters instructions', () => { }); describe('Portal', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Portal.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Portal.deserialize(buf); - expect(inst).toEqual(new Portal(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Portal(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Portal.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Portal.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read Portal correctly', async () => { @@ -125,26 +95,16 @@ describe('Environment getters instructions', () => { }); describe('FeePerL1Gas', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ FeePerL1Gas.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = FeePerL1Gas.deserialize(buf); - expect(inst).toEqual(new FeePerL1Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new FeePerL1Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - FeePerL1Gas.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(FeePerL1Gas.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read FeePerL1Gas correctly', async () => { @@ -154,26 +114,16 @@ describe('Environment getters instructions', () => { }); describe('FeePerL2Gas', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ FeePerL2Gas.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = FeePerL2Gas.deserialize(buf); - expect(inst).toEqual(new FeePerL2Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new FeePerL2Gas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - FeePerL2Gas.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(FeePerL2Gas.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read FeePerL2Gas correctly', async () => { @@ -183,26 +133,16 @@ describe('Environment getters instructions', () => { }); describe('FeePerDAGas', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ FeePerDAGas.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = FeePerDAGas.deserialize(buf); - expect(inst).toEqual(new FeePerDAGas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new FeePerDAGas(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - FeePerDAGas.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(FeePerDAGas.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read FeePerDAGas correctly', async () => { @@ -212,26 +152,16 @@ describe('Environment getters instructions', () => { }); describe('Origin', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Origin.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Origin.deserialize(buf); - expect(inst).toEqual(new Origin(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Origin(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Origin.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Origin.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read Origin correctly', async () => { @@ -241,26 +171,16 @@ describe('Environment getters instructions', () => { }); describe('Sender', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Sender.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Sender.deserialize(buf); - expect(inst).toEqual(new Sender(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Sender(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Sender.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Sender.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read Sender correctly', async () => { @@ -281,26 +201,16 @@ describe('Environment getters instructions', () => { }; describe('chainId', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ ChainId.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = ChainId.deserialize(buf); - expect(inst).toEqual(new ChainId(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new ChainId(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - ChainId.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(ChainId.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read chainId', async () => { @@ -310,26 +220,16 @@ describe('Environment getters instructions', () => { }); describe('version', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Version.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Version.deserialize(buf); - expect(inst).toEqual(new Version(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Version(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Version.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Version.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read version', async () => { @@ -339,26 +239,16 @@ describe('Environment getters instructions', () => { }); describe('block', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ BlockNumber.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = BlockNumber.deserialize(buf); - expect(inst).toEqual(new BlockNumber(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new BlockNumber(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - BlockNumber.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(BlockNumber.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read block number', async () => { @@ -368,26 +258,16 @@ describe('Environment getters instructions', () => { }); describe('timestamp', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Timestamp.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // dstOffset ]); - - const inst = Timestamp.deserialize(buf); - expect(inst).toEqual(new Timestamp(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678)); - }); - - it('Should serialize correctly', () => { const inst = new Timestamp(/*indirect=*/ 0x01, /*dstOffset=*/ 0x12345678); - const expected = Buffer.from([ - Timestamp.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Timestamp.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should read timestamp', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts index 7d29ec9af9d..2395cc32479 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -34,7 +34,7 @@ describe('External Calls', () => { }); describe('Call', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Call.opcode, // opcode 0x01, // indirect @@ -46,23 +46,6 @@ describe('External Calls', () => { ...Buffer.from('e2345678', 'hex'), // retSize ...Buffer.from('f2345678', 'hex'), // successOffset ]); - - const inst = Call.deserialize(buf); - expect(inst).toEqual( - new Call( - /*indirect=*/ 0x01, - /*gasOffset=*/ 0x12345678, - /*addrOffset=*/ 0xa2345678, - /*argsOffset=*/ 0xb2345678, - /*argsSize=*/ 0xc2345678, - /*retOffset=*/ 0xd2345678, - /*retSize=*/ 0xe2345678, - /*successOffset=*/ 0xf2345678, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Call( /*indirect=*/ 0x01, /*gasOffset=*/ 0x12345678, @@ -74,18 +57,8 @@ describe('External Calls', () => { /*successOffset=*/ 0xf2345678, ); - const expected = Buffer.from([ - Call.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // gasOffset - ...Buffer.from('a2345678', 'hex'), // addrOffset - ...Buffer.from('b2345678', 'hex'), // argsOffset - ...Buffer.from('c2345678', 'hex'), // argsSize - ...Buffer.from('d2345678', 'hex'), // retOffset - ...Buffer.from('e2345678', 'hex'), // retSize - ...Buffer.from('f2345678', 'hex'), // successOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Call.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): gas not implemented @@ -145,7 +118,7 @@ describe('External Calls', () => { }); describe('Static Call', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ StaticCall.opcode, // opcode 0x01, // indirect @@ -157,23 +130,6 @@ describe('External Calls', () => { ...Buffer.from('e2345678', 'hex'), // retSize ...Buffer.from('f2345678', 'hex'), // successOffset ]); - - const inst = StaticCall.deserialize(buf); - expect(inst).toEqual( - new StaticCall( - /*indirect=*/ 0x01, - /*gasOffset=*/ 0x12345678, - /*addrOffset=*/ 0xa2345678, - /*argsOffset=*/ 0xb2345678, - /*argsSize=*/ 0xc2345678, - /*retOffset=*/ 0xd2345678, - /*retSize=*/ 0xe2345678, - /*successOffset=*/ 0xf2345678, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new StaticCall( /*indirect=*/ 0x01, /*gasOffset=*/ 0x12345678, @@ -185,18 +141,8 @@ describe('External Calls', () => { /*successOffset=*/ 0xf2345678, ); - const expected = Buffer.from([ - StaticCall.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // gasOffset - ...Buffer.from('a2345678', 'hex'), // addrOffset - ...Buffer.from('b2345678', 'hex'), // argsOffset - ...Buffer.from('c2345678', 'hex'), // argsSize - ...Buffer.from('d2345678', 'hex'), // retOffset - ...Buffer.from('e2345678', 'hex'), // retSize - ...Buffer.from('f2345678', 'hex'), // successOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(StaticCall.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should fail if a static call attempts to touch storage', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 493f025811b..ce4e6165db4 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -18,7 +18,7 @@ describe('Memory instructions', () => { }); describe('SET', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Set.opcode, // opcode 0x01, // indirect @@ -26,19 +26,6 @@ describe('Memory instructions', () => { ...Buffer.from('12345678123456781234567812345678', 'hex'), // const (will be 128 bit) ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Set.deserialize(buf); - expect(inst).toEqual( - new Set( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.FIELD, - /*value=*/ 0x12345678123456781234567812345678n, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new Set( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.FIELD, @@ -46,14 +33,8 @@ describe('Memory instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Set.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // inTag - ...Buffer.from('12345678123456781234567812345678', 'hex'), // const (will be 128 bit) - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Set.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('should correctly set value and tag (uninitialized)', async () => { @@ -94,14 +75,6 @@ describe('Memory instructions', () => { ...Buffer.from('12345678', 'hex'), // aOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Cast.deserialize(buf); - expect(inst).toEqual( - new Cast(/*indirect=*/ 0x01, /*dstTag=*/ TypeTag.FIELD, /*aOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a), - ); - }); - - it('Should serialize correctly', () => { const inst = new Cast( /*indirect=*/ 0x01, /*dstTag=*/ TypeTag.FIELD, @@ -109,14 +82,8 @@ describe('Memory instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - Cast.opcode, // opcode - 0x01, // indirect - TypeTag.FIELD, // dstTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Cast.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should upcast between integral types', () => { @@ -243,28 +210,17 @@ describe('Memory instructions', () => { }); describe('MOV', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ Mov.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // srcOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = Mov.deserialize(buf); - expect(inst).toEqual(new Mov(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a)); - }); - - it('Should serialize correctly', () => { const inst = new Mov(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*dstOffset=*/ 0x3456789a); - const expected = Buffer.from([ - Mov.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // srcOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(Mov.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should move integrals on different memory cells', async () => { @@ -300,20 +256,6 @@ describe('Memory instructions', () => { ...Buffer.from('b2345678', 'hex'), // condOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = CMov.deserialize(buf); - expect(inst).toEqual( - new CMov( - /*indirect=*/ 0x01, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0xa2345678, - /*condOffset=*/ 0xb2345678, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new CMov( /*indirect=*/ 0x01, /*aOffset=*/ 0x12345678, @@ -322,15 +264,8 @@ describe('Memory instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - CMov.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('a2345678', 'hex'), // bOffset - ...Buffer.from('b2345678', 'hex'), // condOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(CMov.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Should move A if COND is true, on different memory cells (integral condition)', async () => { @@ -399,7 +334,7 @@ describe('Memory instructions', () => { }); describe('CALLDATACOPY', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ CalldataCopy.opcode, // opcode 0x01, // indirect @@ -407,19 +342,6 @@ describe('Memory instructions', () => { ...Buffer.from('23456789', 'hex'), // copysize ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - - const inst = CalldataCopy.deserialize(buf); - expect(inst).toEqual( - new CalldataCopy( - /*indirect=*/ 0x01, - /*cdOffset=*/ 0x12345678, - /*copysize=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ), - ); - }); - - it('Should serialize correctly', () => { const inst = new CalldataCopy( /*indirect=*/ 0x01, /*cdOffset=*/ 0x12345678, @@ -427,14 +349,8 @@ describe('Memory instructions', () => { /*dstOffset=*/ 0x3456789a, ); - const expected = Buffer.from([ - CalldataCopy.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // cdOffset - ...Buffer.from('23456789', 'hex'), // copysize - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(CalldataCopy.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Writes nothing if size is 0', async () => { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts index 07c432a9a7c..8f704b036a3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -22,28 +22,17 @@ describe('Storage Instructions', () => { }); describe('SSTORE', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ SStore.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // srcOffset ...Buffer.from('a2345678', 'hex'), // slotOffset ]); - - const inst = SStore.deserialize(buf); - expect(inst).toEqual(new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678)); - }); - - it('Should serialize correctly', () => { const inst = new SStore(/*indirect=*/ 0x01, /*srcOffset=*/ 0x12345678, /*slotOffset=*/ 0xa2345678); - const expected = Buffer.from([ - SStore.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // srcOffset - ...Buffer.from('a2345678', 'hex'), // slotOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(SStore.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Sstore should Write into storage', async () => { @@ -75,28 +64,17 @@ describe('Storage Instructions', () => { }); describe('SLOAD', () => { - it('Should deserialize correctly', () => { + it('Should (de)serialize correctly', () => { const buf = Buffer.from([ SLoad.opcode, // opcode 0x01, // indirect ...Buffer.from('12345678', 'hex'), // slotOffset ...Buffer.from('a2345678', 'hex'), // dstOffset ]); - - const inst = SLoad.deserialize(buf); - expect(inst).toEqual(new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678)); - }); - - it('Should serialize correctly', () => { const inst = new SLoad(/*indirect=*/ 0x01, /*slotOffset=*/ 0x12345678, /*dstOffset=*/ 0xa2345678); - const expected = Buffer.from([ - SLoad.opcode, // opcode - 0x01, // indirect - ...Buffer.from('12345678', 'hex'), // slotOffset - ...Buffer.from('a2345678', 'hex'), // dstOffset - ]); - expect(inst.serialize()).toEqual(expected); + expect(SLoad.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); }); it('Sload should Read into storage', async () => {