Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: block encoding #2719

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<bigint, number>;
}
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/archiver/eth_log_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
}
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/aztec-sandbox/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ services:
image: otterscan/otterscan:develop
# platform: linux/amd64
ports:
- "5100:80"
- '5100:80'
container_name: otterscan
environment:
# otterscan env var is hardcoded to support erigon client
# but it also works for anvil
- ERIGON_URL=http://127.0.0.1:${SANDBOX_ANVIL_PORT:-8545}
# otterscan env var is hardcoded to support erigon client
# but it also works for anvil
- ERIGON_URL=http://127.0.0.1:${SANDBOX_ANVIL_PORT:-8545}
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const setupL1Contracts = async (
/**
* Sets up Private eXecution Environment (PXE).
* @param numberOfAccounts - The number of new accounts to be created once the PXE is initiated.
* @param aztecNode - The instance of an aztec node, if one is required
* @param aztecNode - An instance of Aztec Node.
* @param firstPrivKey - The private key of the first account to be created.
* @param logger - The logger to be used.
* @param useLogSuffix - Whether to add a randomly generated suffix to the PXE debug logs.
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/end-to-end/src/integration_l1_publisher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export async function defaultFetch(
}
if (!resp.ok) {
if (noRetry) {
throw new NoRetryError(responseJson.error.message);
throw new NoRetryError('(JSON-RPC PROPAGATED) ' + responseJson.error.message);
} else {
throw new Error(responseJson.error.message);
throw new Error('(JSON-RPC PROPAGATED) ' + responseJson.error.message);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('L1Publisher', () => {

beforeEach(() => {
l2Block = L2Block.random(42);
l2Inputs = l2Block.encode();
l2Inputs = l2Block.toBufferWithLogs();
l2Proof = Buffer.alloc(0);

txSender = mock<L1PublisherTxSender>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class L1Publisher implements L2BlockReceiver {
*/
public async processL2Block(l2BlockData: L2Block): Promise<boolean> {
const proof = Buffer.alloc(0);
const txData = { proof, inputs: l2BlockData.encode() };
const txData = { proof, inputs: l2BlockData.toBufferWithLogs() };

while (!this.interrupted) {
if (!(await this.checkFeeDistributorBalance())) {
Expand Down
19 changes: 7 additions & 12 deletions yarn-project/types/src/l2_block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,26 @@ 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);

expect(recovered).toEqual(block);
});

it('can serialise an L2 block to JSON and back', () => {
const block = L2Block.random(42);
const serialised = block.toJSON();
const recovered = L2Block.fromJSON(serialised);

expect(recovered).toEqual(block);
});

// TS equivalent of `testComputeKernelLogsIterationWithoutLogs` in `Decoder.t.sol`
it('correctly computes kernel logs hash when there are no logs', () => {
// The following 2 values are copied from `testComputeKernelLogsIterationWithoutLogs` in `Decoder.t.sol`
Expand Down
159 changes: 47 additions & 112 deletions yarn-project/types/src/l2_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +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');
}

toBuffer() {
return serializeToBuffer(
this.globalVariables,
this.startPrivateDataTreeSnapshot,
Expand Down Expand Up @@ -376,65 +374,41 @@ 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})`,
);
}

/**
* Encodes the block as a hex string
* @returns The encoded L2 block data as a hex string.
*/
toString() {
return this.toBuffer().toString(STRING_ENCODING);
return serializeToBuffer(this.toBuffer(), this.newEncryptedLogs, this.newUnencryptedLogs);
}

/**
* Encodes the block as a JSON object.
* @returns The L2 block encoded as a JSON object.
* 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.
*/
toJSON() {
return {
globalVariables: this.globalVariables.toJSON(),
startPrivateDataTreeSnapshot: this.startPrivateDataTreeSnapshot.toString(),
startNullifierTreeSnapshot: this.startNullifierTreeSnapshot.toString(),
startContractTreeSnapshot: this.startContractTreeSnapshot.toString(),
startPublicDataTreeRoot: this.startPublicDataTreeRoot.toString(),
startL1ToL2MessagesTreeSnapshot: this.startL1ToL2MessagesTreeSnapshot.toString(),
startHistoricBlocksTreeSnapshot: this.startHistoricBlocksTreeSnapshot.toString(),
endPrivateDataTreeSnapshot: this.endPrivateDataTreeSnapshot.toString(),
endNullifierTreeSnapshot: this.endNullifierTreeSnapshot.toString(),
endContractTreeSnapshot: this.endContractTreeSnapshot.toString(),
endPublicDataTreeRoot: this.endPublicDataTreeRoot.toString(),
endL1ToL2MessagesTreeSnapshot: this.endL1ToL2MessagesTreeSnapshot.toString(),
endHistoricBlocksTreeSnapshot: this.endHistoricBlocksTreeSnapshot.toString(),
newCommitments: this.newCommitments.map(c => c.toString()),
newNullifiers: this.newNullifiers.map(n => n.toString()),
newPublicDataWrites: this.newPublicDataWrites.map(p => p.toString()),
newL2ToL1Msgs: this.newL2ToL1Msgs.map(m => m.toString()),
newContracts: this.newContracts.map(c => c.toString()),
newContractData: this.newContractData.map(c => c.toString()),
newL1ToL2Messages: this.newL1ToL2Messages.map(m => m.toString()),
newEncryptedLogs: this.newEncryptedLogs?.toJSON() ?? null,
newUnencryptedLogs: this.newUnencryptedLogs?.toJSON() ?? null,
};
toString(): string {
return this.toBuffer().toString(STRING_ENCODING);
}

/**
* 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);
Expand All @@ -457,8 +431,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,
Expand All @@ -482,70 +454,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 fromString(str: string): L2Block {
return L2Block.decode(Buffer.from(str, STRING_ENCODING));
}
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);

static fromJSON(_obj: any): L2Block {
const globalVariables = GlobalVariables.fromJSON(_obj.globalVariables);
const number = Number(globalVariables.blockNumber.value);
const startPrivateDataTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.startPrivateDataTreeSnapshot);
const startNullifierTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.startNullifierTreeSnapshot);
const startContractTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.startContractTreeSnapshot);
const startPublicDataTreeRoot = Fr.fromString(_obj.startPublicDataTreeRoot);
const startL1ToL2MessagesTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.startL1ToL2MessagesTreeSnapshot);
const startHistoricBlocksTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.startHistoricBlocksTreeSnapshot);
const endPrivateDataTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.endPrivateDataTreeSnapshot);
const endNullifierTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.endNullifierTreeSnapshot);
const endContractTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.endContractTreeSnapshot);
const endPublicDataTreeRoot = Fr.fromString(_obj.endPublicDataTreeRoot);
const endL1ToL2MessagesTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.endL1ToL2MessagesTreeSnapshot);
const endHistoricBlocksTreeSnapshot = AppendOnlyTreeSnapshot.fromString(_obj.endHistoricBlocksTreeSnapshot);
const newCommitments = _obj.newCommitments.map((c: string) => Fr.fromString(c));
const newNullifiers = _obj.newNullifiers.map((n: string) => Fr.fromString(n));
const newPublicDataWrites = _obj.newPublicDataWrites.map((p: any) => PublicDataWrite.fromString(p));
const newL2ToL1Msgs = _obj.newL2ToL1Msgs.map((m: string) => Fr.fromString(m));
const newContracts = _obj.newContracts.map((c: string) => Fr.fromString(c));
const newContractData = _obj.newContractData.map((c: any) => ContractData.fromString(c));
const newL1ToL2Messages = _obj.newL1ToL2Messages.map((m: string) => Fr.fromString(m));
const newEncryptedLogs = _obj.newEncryptedLogs ? L2BlockL2Logs.fromJSON(_obj.newEncryptedLogs) : undefined;
const newUnencryptedLogs = _obj.newUnencryptedLogs ? L2BlockL2Logs.fromJSON(_obj.newUnencryptedLogs) : undefined;
block.attachLogs(newEncryptedLogs, LogType.ENCRYPTED);
block.attachLogs(newUnencryptedLogs, LogType.UNENCRYPTED);

return L2Block.fromFields({
number,
globalVariables,
startPrivateDataTreeSnapshot,
startNullifierTreeSnapshot,
startContractTreeSnapshot,
startPublicDataTreeRoot,
startL1ToL2MessagesTreeSnapshot,
startHistoricBlocksTreeSnapshot,
endPrivateDataTreeSnapshot,
endNullifierTreeSnapshot,
endContractTreeSnapshot,
endPublicDataTreeRoot,
endL1ToL2MessagesTreeSnapshot,
endHistoricBlocksTreeSnapshot,
newCommitments,
newNullifiers,
newPublicDataWrites,
newL2ToL1Msgs,
newContracts,
newContractData,
newL1ToL2Messages,
newEncryptedLogs,
newUnencryptedLogs,
});
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.fromBuffer(Buffer.from(str, STRING_ENCODING));
}

/**
Expand Down Expand Up @@ -584,7 +519,7 @@ export class L2Block {
*/
public getBlockHash(): Buffer {
if (!this.blockHash) {
this.blockHash = keccak(this.encode());
this.blockHash = keccak(this.toBufferWithLogs());
}
return this.blockHash;
}
Expand Down