Skip to content

Commit

Permalink
fix: activate sim tree checking in rollup builder
Browse files Browse the repository at this point in the history
  • Loading branch information
cheethas committed Apr 27, 2023
1 parent f81ba76 commit 9014edc
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 27 deletions.
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/structs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const PUBLIC_DATA_TREE_HEIGHT = 254;
export const NULLIFIER_TREE_HEIGHT = 8;
export const L1_TO_L2_MESSAGES_TREE_HEIGHT = 8;
export const L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT = 8;
export const L1_TO_L2_MESSAGES_SUBTREE_INSERTION_HEIGHT = 4;

export const PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT = 8;
export const CONTRACT_TREE_ROOTS_TREE_HEIGHT = 8;
Expand Down
20 changes: 9 additions & 11 deletions yarn-project/circuits.js/src/structs/rollup/root_rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AppendOnlyTreeSnapshot } from './append_only_tree_snapshot.js';
import {
CONTRACT_TREE_ROOTS_TREE_HEIGHT,
L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT,
L1_TO_L2_MESSAGES_SUBTREE_INSERTION_HEIGHT,
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT,
} from '../constants.js';
Expand All @@ -17,27 +18,22 @@ export class RootRollupInputs {

public newHistoricPrivateDataTreeRootSiblingPath: Fr[],
public newHistoricContractDataTreeRootSiblingPath: Fr[],
public newL1ToL2Messages: Fr[],
public newL1ToL2MessageTreeRootSiblingPath: Fr[],
public newHistoricL1ToL2MessageTreeRootSiblingPath: Fr[],
public newL1ToL2Messages: Fr[],
public startL1ToL2MessageTreeSnapshot: AppendOnlyTreeSnapshot,
public startHistoricTreeL1ToL2MessageTreeRootsSnapshot: AppendOnlyTreeSnapshot,
) {
assertLength(this, 'newHistoricPrivateDataTreeRootSiblingPath', PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT);
assertLength(this, 'newHistoricContractDataTreeRootSiblingPath', CONTRACT_TREE_ROOTS_TREE_HEIGHT);
// TODO: the height of this could be wrong
assertLength(this, 'newL1ToL2MessageTreeRootSiblingPath', L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT);
assertLength(this, 'newL1ToL2MessageTreeRootSiblingPath', L1_TO_L2_MESSAGES_SUBTREE_INSERTION_HEIGHT);
assertLength(this, 'newHistoricL1ToL2MessageTreeRootSiblingPath', L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT);
assertLength(this, 'newL1ToL2Messages', NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
}

toBuffer() {
return serializeToBuffer(
this.previousRollupData,
this.newHistoricPrivateDataTreeRootSiblingPath,
this.newHistoricContractDataTreeRootSiblingPath,
this.newL1ToL2MessageTreeRootSiblingPath,
this.newHistoricL1ToL2MessageTreeRootSiblingPath,
this.newL1ToL2Messages,
);
return serializeToBuffer(...RootRollupInputs.getFields(this));
}

static from(fields: FieldsOf<RootRollupInputs>): RootRollupInputs {
Expand All @@ -49,9 +45,11 @@ export class RootRollupInputs {
fields.previousRollupData,
fields.newHistoricPrivateDataTreeRootSiblingPath,
fields.newHistoricContractDataTreeRootSiblingPath,
fields.newL1ToL2Messages,
fields.newL1ToL2MessageTreeRootSiblingPath,
fields.newHistoricL1ToL2MessageTreeRootSiblingPath,
fields.newL1ToL2Messages,
fields.startL1ToL2MessageTreeSnapshot,
fields.startHistoricTreeL1ToL2MessageTreeRootsSnapshot,
] as const;
}
}
Expand Down
7 changes: 5 additions & 2 deletions yarn-project/circuits.js/src/tests/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
KERNEL_PUBLIC_CALL_STACK_LENGTH,
L1_MSG_STACK_LENGTH,
L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT,
L1_TO_L2_MESSAGES_SUBTREE_INSERTION_HEIGHT,
L1_TO_L2_MESSAGES_TREE_HEIGHT,
NEW_COMMITMENTS_LENGTH,
NEW_NULLIFIERS_LENGTH,
Expand Down Expand Up @@ -396,9 +397,11 @@ export function makeRootRollupInputs(seed = 0) {
[makePreviousBaseRollupData(seed), makePreviousBaseRollupData(seed + 0x1000)],
range(PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT, 0x2000).map(fr),
range(CONTRACT_TREE_ROOTS_TREE_HEIGHT, 0x2100).map(fr),
range(L1_TO_L2_MESSAGES_TREE_HEIGHT, 0x2100).map(fr),
range(L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT, 0x2100).map(fr),
range(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 0x2100).map(fr),
range(L1_TO_L2_MESSAGES_SUBTREE_INSERTION_HEIGHT, 0x2100).map(fr),
range(L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT, 0x2100).map(fr),
makeAppendOnlyTreeSnapshot(seed + 0x2200),
makeAppendOnlyTreeSnapshot(seed + 0x2300),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('sequencer/circuit_block_builder', () => {
prover = mock<RollupProver>();
builder = new TestSubject(builderDb, vks, simulator, prover);

mockL1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0);
mockL1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n));

// Populate root trees with first roots from the empty trees
// TODO: Should this be responsibility of the MerkleTreeDb init?
Expand All @@ -90,6 +90,7 @@ describe('sequencer/circuit_block_builder', () => {
for (const [newTree, rootTree] of [
[MerkleTreeId.PRIVATE_DATA_TREE, MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE],
[MerkleTreeId.CONTRACT_TREE, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE],
[MerkleTreeId.L1_TO_L2_MESSAGES_TREE, MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE],
] as const) {
const newTreeInfo = await expectsDb.getTreeInfo(newTree);
await expectsDb.appendLeaves(rootTree, [newTreeInfo.root]);
Expand All @@ -111,6 +112,12 @@ describe('sequencer/circuit_block_builder', () => {
}
};

// TODO: could just inline this?
const updateL1ToL2MessagesTree = async (l1ToL2Messages: Fr[]) => {
const asBuffer = l1ToL2Messages.map(m => m.toBuffer());
await expectsDb.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, asBuffer);
};

const getTreeSnapshot = async (tree: MerkleTreeId) => {
const treeInfo = await expectsDb.getTreeInfo(tree);
return new AppendOnlyTreeSnapshot(Fr.fromBuffer(treeInfo.root), Number(treeInfo.size));
Expand Down Expand Up @@ -158,7 +165,9 @@ describe('sequencer/circuit_block_builder', () => {
baseRollupOutputRight.endPrivateDataTreeSnapshot = await getTreeSnapshot(MerkleTreeId.PRIVATE_DATA_TREE);
baseRollupOutputRight.endPublicDataTreeSnapshot = await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE);

// Update l1 to l2 data tree
// And update the root trees now to create proper output to the root rollup circuit
await updateL1ToL2MessagesTree(mockL1ToL2Messages);
await updateRootTrees();
rootRollupOutput.endContractTreeSnapshot = await getTreeSnapshot(MerkleTreeId.CONTRACT_TREE);
rootRollupOutput.endNullifierTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE);
Expand All @@ -175,6 +184,10 @@ describe('sequencer/circuit_block_builder', () => {
MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE,
);

console.log('rro inside');
console.log(rootRollupOutput);
console.log(rootRollupOutput.endTreeOfHistoricL1ToL2MessageTreeRootsSnapshot.root.toBuffer().toString('hex'));

// Actually build a block!
const txs = [tx, await makeEmptyProcessedTx(), await makeEmptyProcessedTx(), await makeEmptyProcessedTx()];
const [l2Block, proof] = await builder.buildL2Block(blockNumber, txs, mockL1ToL2Messages);
Expand Down Expand Up @@ -279,7 +292,7 @@ describe('sequencer/circuit_block_builder', () => {

const [l2Block] = await builder.buildL2Block(blockNumber, txs, mockL1ToL2Messages);
expect(l2Block.number).toEqual(blockNumber);
});
}, 20000);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ import { RollupSimulator } from '../simulator/index.js';
import { ProcessedTx } from '../sequencer/processed_tx.js';
import { BlockBuilder } from './index.js';

// TODO: where to put this type
type AllowedTreeNames<T extends BaseOrMergeRollupPublicInputs | RootRollupPublicInputs> =
T extends RootRollupPublicInputs
? 'PrivateData' | 'Contract' | 'Nullifier' | 'L1ToL2Message'
: 'PrivateData' | 'Contract' | 'Nullifier';

// TODO: triple check and clean this type casting
type OutputWithTreeSnapshot<T extends BaseOrMergeRollupPublicInputs | RootRollupPublicInputs> = {
[K in `end${AllowedTreeNames<T>}TreeSnapshot`]: AppendOnlyTreeSnapshot;
};

const frToBigInt = (fr: Fr) => toBigIntBE(fr.toBuffer());
const bigintToFr = (num: bigint) => new Fr(num);
const bigintToNum = (num: bigint) => Number(num);
Expand Down Expand Up @@ -249,14 +260,30 @@ export class CircuitBlockBuilder implements BlockBuilder {
this.debug(`Running root rollup circuit`);
const rootInput = await this.getRootRollupInput(...left, ...right, newL1ToL2Messages);

// Update the local trees to include the new l1 to l2 messages
// TODO:
await this.db.appendLeaves(
MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
newL1ToL2Messages.map(m => m.toBuffer()),
);

console.log('root rollup inputs');
console.log(rootInput);

// Simulate and get proof for the root circuit
const rootOutput = await this.simulator.rootRollupCircuit(rootInput);

console.log('root output');
console.log(rootOutput);

const rootProof = await this.prover.getRootRollupProof(rootInput, rootOutput);

// Update the root trees with the latest data and contract tree roots,
// and validate them against the output of the root circuit simulation
this.debug(`Updating and validating root trees`);
await this.updateRootTrees();
console.log('root output in the sim');
console.log(rootOutput);
await this.validateRootOutput(rootOutput);

return [rootOutput, rootProof];
Expand All @@ -267,6 +294,7 @@ export class CircuitBlockBuilder implements BlockBuilder {
for (const [newTree, rootTree] of [
[MerkleTreeId.PRIVATE_DATA_TREE, MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE],
[MerkleTreeId.CONTRACT_TREE, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE],
[MerkleTreeId.L1_TO_L2_MESSAGES_TREE, MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE],
] as const) {
const newTreeInfo = await this.db.getTreeInfo(newTree);
await this.db.appendLeaves(rootTree, [newTreeInfo.root]);
Expand All @@ -288,28 +316,34 @@ export class CircuitBlockBuilder implements BlockBuilder {
this.validateTrees(rootOutput),
this.validateRootTree(rootOutput, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE, 'Contract'),
this.validateRootTree(rootOutput, MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE, 'PrivateData'),
this.validateRootTree(rootOutput, MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE, 'L1ToL2Message'),
this.validateTree(rootOutput, MerkleTreeId.L1_TO_L2_MESSAGES_TREE, 'L1ToL2Message'),
]);
}

// Helper for validating a roots tree against a circuit simulation output
protected async validateRootTree(
rootOutput: RootRollupPublicInputs,
treeId: MerkleTreeId,
name: 'Contract' | 'PrivateData',
name: 'Contract' | 'PrivateData' | 'L1ToL2Message',
) {
const localTree = await this.getTreeSnapshot(treeId);
const simulatedTree = rootOutput[`endTreeOfHistoric${name}TreeRootsSnapshot`];
this.validateSimulatedTree(localTree, simulatedTree, name, `Roots ${name}`);
}

// Helper for validating a non-roots tree against a circuit simulation output
protected async validateTree(
output: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs,
protected async validateTree<T extends BaseOrMergeRollupPublicInputs | RootRollupPublicInputs>(
output: T,
treeId: MerkleTreeId,
name: 'PrivateData' | 'Contract' | 'Nullifier',
name: AllowedTreeNames<T>,
) {
if ('endL1ToL2MessageTreeSnapshot' in output && !(output instanceof RootRollupPublicInputs)) {
throw new Error(`The name 'L1ToL2Message' can only be used when output is of type RootRollupPublicInputs`);
}

const localTree = await this.getTreeSnapshot(treeId);
const simulatedTree = output[`end${name}TreeSnapshot`];
const simulatedTree = (output as OutputWithTreeSnapshot<T>)[`end${name}TreeSnapshot`];
this.validateSimulatedTree(localTree, simulatedTree, name);
}

Expand All @@ -321,6 +355,8 @@ export class CircuitBlockBuilder implements BlockBuilder {
label?: string,
) {
if (!simulatedTree.root.toBuffer().equals(localTree.root.toBuffer())) {
console.log('local: ', localTree);
console.log('sim: ', simulatedTree);
throw new Error(`${label ?? name} tree root mismatch (local ${localTree.root}, simulated ${simulatedTree.root})`);
}
if (simulatedTree.nextAvailableLeafIndex !== localTree.nextAvailableLeafIndex) {
Expand Down Expand Up @@ -364,8 +400,20 @@ export class CircuitBlockBuilder implements BlockBuilder {
MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE,
);

// TODO: not sure if this is the correct way to fetch this
const newL1ToL2MessageTreeRootSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE);
// TODO: UPDATE 4 to be a constant like in base rollups!
const newL1ToL2MessageTreeRootSiblingPath = await this.getSubtreeSiblingPath(
MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
4,
);

// Get tree snapshots
const startL1ToL2MessageTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE);

console.log('startL1ToL2MessageTreeSnapshot');
console.log(startL1ToL2MessageTreeSnapshot);
const startHistoricTreeL1ToL2MessageTreeRootsSnapshot = await this.getTreeSnapshot(
MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE,
);

return RootRollupInputs.from({
previousRollupData,
Expand All @@ -374,6 +422,8 @@ export class CircuitBlockBuilder implements BlockBuilder {
newL1ToL2Messages,
newHistoricL1ToL2MessageTreeRootSiblingPath,
newL1ToL2MessageTreeRootSiblingPath,
startL1ToL2MessageTreeSnapshot,
startHistoricTreeL1ToL2MessageTreeRootsSnapshot,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class StandaloneBlockBuilder implements BlockBuilder {
for (const tx of txs) {
await this.updateTrees(tx);
}
await this.updateL1ToL2MessagesTree(newL1ToL2Messages);

await this.updateRootTrees();

Expand Down Expand Up @@ -104,6 +105,7 @@ export class StandaloneBlockBuilder implements BlockBuilder {
computeContractLeaf(wasm, x).toBuffer(),
);

// TODO: why do these need a loop? come back to
for (let i = 0; i < KERNEL_NEW_COMMITMENTS_LENGTH; i++) {
await this.db.appendLeaves(MerkleTreeId.PRIVATE_DATA_TREE, [dataTreeLeaves[i]]);
}
Expand All @@ -115,10 +117,17 @@ export class StandaloneBlockBuilder implements BlockBuilder {
}
}

private async updateL1ToL2MessagesTree(l1ToL2Messages: Fr[]) {
const leaves = l1ToL2Messages.map((x: Fr) => x.toBuffer());
await this.db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leaves);
}

private async updateRootTrees() {
const newDataTreeInfo = await this.getTreeSnapshot(MerkleTreeId.PRIVATE_DATA_TREE);
const newContractsTreeInfo = await this.getTreeSnapshot(MerkleTreeId.CONTRACT_TREE);
const newL1ToL2MessagesTreeInfo = await this.getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE);
await this.db.appendLeaves(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE, [newContractsTreeInfo.root.toBuffer()]);
await this.db.appendLeaves(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE, [newDataTreeInfo.root.toBuffer()]);
await this.db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE, [newL1ToL2MessagesTreeInfo.root.toBuffer()]);
}
}
6 changes: 3 additions & 3 deletions yarn-project/sequencer-client/src/sequencer/sequencer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, makeEmptyProof } from '@aztec/circuits.js';
import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, makeEmptyProof } from '@aztec/circuits.js';
import { P2P, P2PClientState } from '@aztec/p2p';
import { L2Block, PrivateTx, Tx, UnverifiedData } from '@aztec/types';
import { MerkleTreeId, MerkleTreeOperations, WorldStateRunningState, WorldStateSynchroniser } from '@aztec/world-state';
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('sequencer', () => {
lastBlockNumber + 1,
expectedTxHashes.map(hash => expect.objectContaining({ hash })),
// TODO: longer term solution to this
Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0),
Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)),
);
expect(publisher.processL2Block).toHaveBeenCalledWith(block);
expect(publisher.processUnverifiedData).toHaveBeenCalledWith(lastBlockNumber + 1, expectedUnverifiedData);
Expand Down Expand Up @@ -103,7 +103,7 @@ describe('sequencer', () => {
lastBlockNumber + 1,
expectedTxHashes.map(hash => expect.objectContaining({ hash })),
// TODO: longer term solution to this
Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0),
Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)),
);
expect(publisher.processL2Block).toHaveBeenCalledWith(block);
expect(publisher.processUnverifiedData).toHaveBeenCalledWith(lastBlockNumber + 1, expectedUnverifiedData);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class Sequencer {
protected takeL1ToL2MessagesFromContract(): Fr[] {
// TODO: use protective typing for the l1 to l2 messages array. - remove Fr import
// TODO: dont make all zeros
return new Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0);
return new Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/world-state/src/world-state-db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export enum MerkleTreeId {
PRIVATE_DATA_TREE = 3,
PRIVATE_DATA_TREE_ROOTS_TREE = 4,
PUBLIC_DATA_TREE = 5,
L1_TO_L2_MESSAGES_TREE,
L1_TO_L2_MESSAGES_TREE = 6,
L1_TO_L2_MESSAGES_ROOTS_TREE = 7,
}

Expand Down

0 comments on commit 9014edc

Please sign in to comment.