diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 6bf50ce7aee..89dd1a0a433 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -296,7 +296,7 @@ function makeL1ToL2MessageCancelledEvents(l1BlockNum: bigint, entryKeys: string[ */ function makeRollupTx(l2Block: L2Block) { const proof = `0x`; - const block = toHex(l2Block.encode()); + const block = toHex(l2Block.toBufferWithLogs()); const input = encodeFunctionData({ abi: RollupAbi, functionName: 'process', args: [proof, block] }); return { input } as Transaction; } diff --git a/yarn-project/archiver/src/archiver/eth_log_handlers.ts b/yarn-project/archiver/src/archiver/eth_log_handlers.ts index 30c6bbcea3a..ebbbd926748 100644 --- a/yarn-project/archiver/src/archiver/eth_log_handlers.ts +++ b/yarn-project/archiver/src/archiver/eth_log_handlers.ts @@ -104,7 +104,7 @@ async function getBlockFromCallData( }); if (functionName !== 'process') throw new Error(`Unexpected method called ${functionName}`); const [, l2BlockHex] = args! as [Hex, Hex]; - const block = L2Block.decode(Buffer.from(hexToBytes(l2BlockHex))); + const block = L2Block.fromBufferWithLogs(Buffer.from(hexToBytes(l2BlockHex))); if (BigInt(block.number) !== l2BlockNum) { throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${block.number}`); } diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index adc9c01d7ae..5875d3af99a 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -315,11 +315,11 @@ describe('L1Publisher integration', () => { const expectedData = encodeFunctionData({ abi: RollupAbi, functionName: 'process', - args: [`0x${l2Proof.toString('hex')}`, `0x${block.encode().toString('hex')}`], + args: [`0x${l2Proof.toString('hex')}`, `0x${block.toBufferWithLogs().toString('hex')}`], }); expect(ethTx.input).toEqual(expectedData); - const decoderArgs = [`0x${block.encode().toString('hex')}`] as const; + const decoderArgs = [`0x${block.toBufferWithLogs().toString('hex')}`] as const; const decodedHashes = await decoderHelper.read.computeDiffRootAndMessagesHash(decoderArgs); const decodedRes = await decoderHelper.read.decode(decoderArgs); const stateInRollup = await rollup.read.rollupStateHash(); @@ -387,11 +387,11 @@ describe('L1Publisher integration', () => { const expectedData = encodeFunctionData({ abi: RollupAbi, functionName: 'process', - args: [`0x${l2Proof.toString('hex')}`, `0x${block.encode().toString('hex')}`], + args: [`0x${l2Proof.toString('hex')}`, `0x${block.toBufferWithLogs().toString('hex')}`], }); expect(ethTx.input).toEqual(expectedData); - const decoderArgs = [`0x${block.encode().toString('hex')}`] as const; + const decoderArgs = [`0x${block.toBufferWithLogs().toString('hex')}`] as const; const decodedHashes = await decoderHelper.read.computeDiffRootAndMessagesHash(decoderArgs); const decodedRes = await decoderHelper.read.decode(decoderArgs); const stateInRollup = await rollup.read.rollupStateHash(); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index df3c48e550c..7ffe7a97a5f 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -16,7 +16,7 @@ describe('L1Publisher', () => { beforeEach(() => { l2Block = L2Block.random(42); - l2Inputs = l2Block.encode(); + l2Inputs = l2Block.toBufferWithLogs(); l2Proof = Buffer.alloc(0); txSender = mock(); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 4c608cdd352..1294f698555 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -124,7 +124,7 @@ export class L1Publisher implements L2BlockReceiver { */ public async processL2Block(l2BlockData: L2Block): Promise { const proof = Buffer.alloc(0); - const txData = { proof, inputs: l2BlockData.encode() }; + const txData = { proof, inputs: l2BlockData.toBufferWithLogs() }; while (!this.interrupted) { if (!(await this.checkFeeDistributorBalance())) { diff --git a/yarn-project/types/src/l2_block.test.ts b/yarn-project/types/src/l2_block.test.ts index 959849fed67..ec1dd54ccc0 100644 --- a/yarn-project/types/src/l2_block.test.ts +++ b/yarn-project/types/src/l2_block.test.ts @@ -2,17 +2,20 @@ import { TxL2Logs } from './index.js'; import { L2Block } from './l2_block.js'; describe('L2Block', () => { - it('can encode a L2 block data object to buffer and back', () => { + it('can serialize an L2 block with logs to a buffer and back', () => { const block = L2Block.random(42); - const buffer = block.encode(); - const recovered = L2Block.decode(buffer); + const buffer = block.toBufferWithLogs(); + const recovered = L2Block.fromBufferWithLogs(buffer); expect(recovered).toEqual(block); }); - it('can encode a L2 block to string and back', () => { + it('can serialize an L2 block without logs to a buffer and back', () => { const block = L2Block.random(42); + block.newEncryptedLogs = undefined; + block.newUnencryptedLogs = undefined; + const serialised = block.toString(); const recovered = L2Block.fromString(serialised); diff --git a/yarn-project/types/src/l2_block.ts b/yarn-project/types/src/l2_block.ts index c678593c3aa..52e13ec82d2 100644 --- a/yarn-project/types/src/l2_block.ts +++ b/yarn-project/types/src/l2_block.ts @@ -341,16 +341,12 @@ export class L2Block { } /** - * Encode the L2 block data into a buffer that can be pushed to the rollup contract. - * @returns The encoded L2 block data. + * Serializes a block without logs to a buffer. + * @remarks This is used when the block is being served via JSON-RPC because the logs are expected to be served + * separately. + * @returns A serialized L2 block without logs. */ - encode(): Buffer { - if (this.newEncryptedLogs === undefined || this.newUnencryptedLogs === undefined) { - throw new Error( - `newEncryptedLogs and newUnencryptedLogs must be defined when encoding L2BlockData (block ${this.number})`, - ); - } - + toBuffer() { return serializeToBuffer( this.globalVariables, this.startPrivateDataTreeSnapshot, @@ -378,24 +374,31 @@ export class L2Block { this.newContractData, this.newL1ToL2Messages.length, this.newL1ToL2Messages, - this.newEncryptedLogs, - this.newUnencryptedLogs, ); } /** - * Alias for encode. - * @returns The encoded L2 block data. + * Serializes a block with logs to a buffer. + * @remarks This is used when the block is being submitted on L1. + * @returns A serialized L2 block with logs. */ - toBuffer() { - return this.encode(); + toBufferWithLogs(): Buffer { + if (this.newEncryptedLogs === undefined || this.newUnencryptedLogs === undefined) { + throw new Error( + `newEncryptedLogs and newUnencryptedLogs must be defined when encoding L2BlockData (block ${this.number})`, + ); + } + + return serializeToBuffer(this.toBuffer(), this.newEncryptedLogs, this.newUnencryptedLogs); } /** - * Encodes the block as a hex string - * @returns The encoded L2 block data as a hex string. + * Serializes a block without logs to a string. + * @remarks This is used when the block is being served via JSON-RPC because the logs are expected to be served + * separately. + * @returns A serialized L2 block without logs. */ - toString() { + toString(): string { return this.toBuffer().toString(STRING_ENCODING); } @@ -431,12 +434,12 @@ export class L2Block { } /** - * Decode the L2 block data from a buffer. - * @param encoded - The encoded L2 block data. - * @returns The decoded L2 block data. + * Deserializes L2 block without logs from a buffer. + * @param buf - A serialized L2 block. + * @returns Deserialized L2 block. */ - static decode(encoded: Buffer | BufferReader) { - const reader = BufferReader.asReader(encoded); + static fromBuffer(buf: Buffer | BufferReader) { + const reader = BufferReader.asReader(buf); const globalVariables = reader.readObject(GlobalVariables); const number = Number(globalVariables.blockNumber.value); const startPrivateDataTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); @@ -459,8 +462,6 @@ export class L2Block { const newContractData = reader.readArray(newContracts.length, ContractData); // TODO(sean): could an optimisation of this be that it is encoded such that zeros are assumed const newL1ToL2Messages = reader.readVector(Fr); - const newEncryptedLogs = reader.readObject(L2BlockL2Logs); - const newUnencryptedLogs = reader.readObject(L2BlockL2Logs); return L2Block.fromFields({ number, @@ -484,18 +485,33 @@ export class L2Block { newContracts, newContractData, newL1ToL2Messages, - newEncryptedLogs, - newUnencryptedLogs, }); } /** - * Decode the L2 block from a string - * @param str - The serialised L2 block - * @returns An L2 block + * Deserializes L2 block with logs from a buffer. + * @param buf - A serialized L2 block. + * @returns Deserialized L2 block. + */ + static fromBufferWithLogs(buf: Buffer | BufferReader) { + const reader = BufferReader.asReader(buf); + const block = L2Block.fromBuffer(reader); + const newEncryptedLogs = reader.readObject(L2BlockL2Logs); + const newUnencryptedLogs = reader.readObject(L2BlockL2Logs); + + block.attachLogs(newEncryptedLogs, LogType.ENCRYPTED); + block.attachLogs(newUnencryptedLogs, LogType.UNENCRYPTED); + + return block; + } + + /** + * Deserializes L2 block without logs from a buffer. + * @param str - A serialized L2 block. + * @returns Deserialized L2 block. */ static fromString(str: string): L2Block { - return L2Block.decode(Buffer.from(str, STRING_ENCODING)); + return L2Block.fromBuffer(Buffer.from(str, STRING_ENCODING)); } static fromJSON(_obj: any): L2Block { @@ -586,7 +602,7 @@ export class L2Block { */ public getBlockHash(): Buffer { if (!this.blockHash) { - this.blockHash = keccak(this.encode()); + this.blockHash = keccak(this.toBufferWithLogs()); } return this.blockHash; }