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

feat: serialise L2Block to JSON #2496

Merged
merged 1 commit into from
Sep 25, 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
18 changes: 18 additions & 0 deletions yarn-project/circuits.js/src/structs/global_variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,29 @@ export class GlobalVariables {
return new GlobalVariables(reader.readFr(), reader.readFr(), reader.readFr(), reader.readFr());
}

static fromJSON(obj: any): GlobalVariables {
return new GlobalVariables(
Fr.fromString(obj.chainId),
Fr.fromString(obj.version),
Fr.fromString(obj.blockNumber),
Fr.fromString(obj.timestamp),
);
}

static getFields(fields: FieldsOf<GlobalVariables>) {
return [fields.chainId, fields.version, fields.blockNumber, fields.timestamp] as const;
}

toBuffer() {
return serializeToBuffer(...GlobalVariables.getFields(this));
}

toJSON() {
return {
chainId: this.chainId.toString(),
version: this.version.toString(),
blockNumber: this.blockNumber.toString(),
timestamp: this.timestamp.toString(),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Fr } from '@aztec/foundation/fields';
import { BufferReader } from '@aztec/foundation/serialize';

import { serializeToBuffer } from '../../utils/serialize.js';
import { UInt32 } from '../shared.js';
import { STRING_ENCODING, UInt32 } from '../shared.js';

/**
* Snapshot of an append only tree.
Expand Down Expand Up @@ -31,11 +31,19 @@ export class AppendOnlyTreeSnapshot {
return serializeToBuffer(this.root, this.nextAvailableLeafIndex);
}

toString(): string {
return this.toBuffer().toString(STRING_ENCODING);
}

static fromBuffer(buffer: Buffer | BufferReader): AppendOnlyTreeSnapshot {
const reader = BufferReader.asReader(buffer);
return new AppendOnlyTreeSnapshot(reader.readFr(), reader.readNumber());
}

static fromString(str: string): AppendOnlyTreeSnapshot {
return AppendOnlyTreeSnapshot.fromBuffer(Buffer.from(str, STRING_ENCODING));
}

static empty() {
return new AppendOnlyTreeSnapshot(Fr.ZERO, 0);
}
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/circuits.js/src/structs/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ export enum RollupTypes {
Merge = 1,
Root = 2,
}

/**
* String encoding of serialised buffer data
*/
export const STRING_ENCODING: BufferEncoding = 'hex';
8 changes: 8 additions & 0 deletions yarn-project/types/src/l2_block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ describe('L2Block', () => {
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
89 changes: 84 additions & 5 deletions yarn-project/types/src/l2_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MAX_NEW_NULLIFIERS_PER_TX,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
STRING_ENCODING,
} from '@aztec/circuits.js';
import { makeAppendOnlyTreeSnapshot, makeGlobalVariables } from '@aztec/circuits.js/factories';
import { BufferReader, serializeToBuffer } from '@aztec/circuits.js/utils';
Expand All @@ -19,11 +20,6 @@ import times from 'lodash.times';
import { ContractData, L2Tx, LogType, PublicDataWrite, TxL2Logs } from './index.js';
import { L2BlockL2Logs } from './logs/l2_block_l2_logs.js';

/**
* String encoding of serialised L2 block data.
*/
const STRING_ENCODING: BufferEncoding = 'hex';

/**
* The data that makes up the rollup proof, with encoder decoder functions.
* TODO: Reuse data types and serialization functions from circuits package.
Expand Down Expand Up @@ -401,6 +397,37 @@ export class L2Block {
return this.toBuffer().toString(STRING_ENCODING);
}

/**
* Encodes the block as a JSON object.
* @returns The L2 block encoded as a JSON object.
*/
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,
};
}

/**
* Decode the L2 block data from a buffer.
* @param encoded - The encoded L2 block data.
Expand Down Expand Up @@ -469,6 +496,58 @@ export class L2Block {
return L2Block.decode(Buffer.from(str, STRING_ENCODING));
}

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;

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,
});
}

/**
* Helper function to attach logs related to a block.
* @param logs - The logs to be attached to a block.
Expand Down
18 changes: 18 additions & 0 deletions yarn-project/types/src/public_data_write.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { STRING_ENCODING } from '@aztec/circuits.js';
import { serializeToBuffer } from '@aztec/circuits.js/utils';
import { Fr } from '@aztec/foundation/fields';
import { BufferReader } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -43,6 +44,14 @@ export class PublicDataWrite {
return serializeToBuffer(this.leafIndex, this.newValue);
}

/**
* Serialises the operation to a string.
* @returns A string representation of the operation.
*/
toString(): string {
return this.toBuffer().toString(STRING_ENCODING);
}

/**
* Checks if the public data write operation is empty.
* @returns True if the public data write operation is empty, false otherwise.
Expand All @@ -61,6 +70,15 @@ export class PublicDataWrite {
return new PublicDataWrite(reader.readFr(), reader.readFr());
}

/**
* Creates a new public data write operation from the given string.
* @param str - The serialised string
* @returns A new public data write operation instance.
*/
static fromString(str: string): PublicDataWrite {
return PublicDataWrite.fromBuffer(Buffer.from(str, STRING_ENCODING));
}

/**
* Creates an empty public data write operation.
* @returns A new public data write operation instance.
Expand Down