From 880b7027a3e0a264e2a2bfb76918f65e47a59b82 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 10:44:45 +0000 Subject: [PATCH 01/40] wip abstracting leaf and preimage in indexed --- .../src/interfaces/indexed_tree.ts | 45 ++- yarn-project/merkle-tree/src/load_tree.ts | 5 +- yarn-project/merkle-tree/src/new_tree.ts | 17 +- .../src/sparse_tree/sparse_tree.test.ts | 5 +- .../standard_indexed_tree.ts | 275 +++++++++--------- .../test/standard_indexed_tree.test.ts | 33 ++- .../test/standard_indexed_tree_with_append.ts | 32 +- .../src/standard_tree/standard_tree.test.ts | 6 +- .../contracts/test_contract/src/interface.nr | 9 +- yarn-project/types/src/interfaces/index.ts | 4 +- .../types/src/interfaces/indexed_tree.ts | 36 +++ .../types/src/interfaces/leaf_data.ts | 17 -- .../types/src/interfaces/nullifier_tree.ts | 107 +++++++ .../types/src/interfaces/nullifier_witness.ts | 41 --- .../types/src/interfaces/state_provider.ts | 2 +- .../merkle_tree_operations_facade.ts | 14 +- .../src/world-state-db/merkle_tree_db.ts | 11 +- .../src/world-state-db/merkle_trees.ts | 19 +- 18 files changed, 412 insertions(+), 266 deletions(-) create mode 100644 yarn-project/types/src/interfaces/indexed_tree.ts delete mode 100644 yarn-project/types/src/interfaces/leaf_data.ts create mode 100644 yarn-project/types/src/interfaces/nullifier_tree.ts delete mode 100644 yarn-project/types/src/interfaces/nullifier_witness.ts diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index 46c13f49bd91..17c66a9100d5 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -1,16 +1,39 @@ -import { LeafData, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage, SiblingPath } from '@aztec/types'; -import { LowLeafWitnessData } from '../index.js'; import { AppendOnlyTree } from './append_only_tree.js'; +/* eslint-disable */ + +/** + * All of the data to be return during batch insertion. + */ +export interface LowLeafWitnessData { + /** + * Preimage of the low nullifier that proves non membership. + */ + leafData: IndexedTreeLeafPreimage; + /** + * Sibling path to prove membership of low nullifier. + */ + siblingPath: SiblingPath; + /** + * The index of low nullifier. + */ + index: bigint; +} + /** * The result of a batch insertion in an indexed merkle tree. */ -export interface BatchInsertionResult { +export interface BatchInsertionResult< + TreeHeight extends number, + SubtreeSiblingPathHeight extends number, + Leaf extends IndexedTreeLeaf, +> { /** * Data for the leaves to be updated when inserting the new ones. */ - lowLeavesWitnessData?: LowLeafWitnessData[]; + lowLeavesWitnessData?: LowLeafWitnessData[]; /** * Sibling path "pointing to" where the new subtree should be inserted into the tree. */ @@ -28,14 +51,14 @@ export interface BatchInsertionResult extends AppendOnlyTree { /** * Finds the index of the largest leaf whose value is less than or equal to the provided value. * @param newValue - The new value to be inserted into the tree. * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`. */ - findIndexOfPreviousValue( + findIndexOfPreviousKey( newValue: bigint, includeUncommitted: boolean, ): { @@ -50,12 +73,12 @@ export interface IndexedTree extends AppendOnlyTree { }; /** - * Gets the latest LeafData copy. - * @param index - Index of the leaf of which to obtain the LeafData copy. + * Gets the latest LeafPreimage copy. + * @param index - Index of the leaf of which to obtain the LeafPreimage copy. * @param includeUncommitted - If true, the uncommitted changes are included in the search. - * @returns A copy of the leaf data at the given index or undefined if the leaf was not found. + * @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found. */ - getLatestLeafDataCopy(index: number, includeUncommitted: boolean): LeafData | undefined; + getLatestLeafPreimageCopy(index: number, includeUncommitted: boolean): IndexedTreeLeafPreimage | undefined; /** * Batch insert multiple leaves into the tree. @@ -67,5 +90,5 @@ export interface IndexedTree extends AppendOnlyTree { leaves: Buffer[], subtreeHeight: SubtreeHeight, includeUncommitted: boolean, - ): Promise>; + ): Promise>; } diff --git a/yarn-project/merkle-tree/src/load_tree.ts b/yarn-project/merkle-tree/src/load_tree.ts index baabe852735b..7bbb85634aa1 100644 --- a/yarn-project/merkle-tree/src/load_tree.ts +++ b/yarn-project/merkle-tree/src/load_tree.ts @@ -13,14 +13,15 @@ import { TreeBase, decodeMeta } from './tree_base.js'; * @returns The newly created tree. */ export async function loadTree( - c: new (...args: any[]) => T, + c: (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => T, db: LevelUp, hasher: Hasher, name: string, ): Promise { const meta: Buffer = await db.get(name); const { root, depth, size } = decodeMeta(meta); - const tree = new c(db, hasher, name, depth, size, root); + + const tree = c(db, hasher, name, depth, size, root); await tree.initFromDb(); return tree; } diff --git a/yarn-project/merkle-tree/src/new_tree.ts b/yarn-project/merkle-tree/src/new_tree.ts index f1cd4c2d3b50..819c0409c6c9 100644 --- a/yarn-project/merkle-tree/src/new_tree.ts +++ b/yarn-project/merkle-tree/src/new_tree.ts @@ -4,6 +4,19 @@ import { LevelUp } from 'levelup'; import { TreeBase } from './tree_base.js'; +/** + * Wraps a constructor in a regular builder + * @param clazz - The class to be instantiated. + * @returns A builder function. + */ +export function builder( + clazz: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => T, +) { + return (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => { + return new clazz(db, hasher, name, depth, size, root); + }; +} + /** * Creates a new tree. * @param c - The class of the tree to be instantiated. @@ -15,14 +28,14 @@ import { TreeBase } from './tree_base.js'; * @returns The newly created tree. */ export async function newTree( - c: new (...args: any[]) => T, + c: (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => T, db: LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1, ): Promise { - const tree = new c(db, hasher, name, depth, 0n, undefined); + const tree = c(db, hasher, name, depth, 0n); await tree.init(prefilledSize); return tree; } diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts index bd4f1e6bd0b2..7f1df12cc0f1 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts @@ -7,6 +7,7 @@ import { default as levelup } from 'levelup'; import { INITIAL_LEAF, newTree } from '../index.js'; import { UpdateOnlyTree } from '../interfaces/update_only_tree.js'; import { loadTree } from '../load_tree.js'; +import { builder } from '../new_tree.js'; import { Pedersen } from '../pedersen.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; @@ -21,11 +22,11 @@ const createDb = async ( name: string, depth: number, ): Promise => { - return await newTree(SparseTree, levelUp, hasher, name, depth); + return await newTree(builder(SparseTree), levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string): Promise => { - return await loadTree(SparseTree, levelUp, hasher, name); + return await loadTree(builder(SparseTree), levelUp, hasher, name); }; const TEST_TREE_DEPTH = 3; diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index f9d44353fa94..b18e00275ae3 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -1,92 +1,83 @@ import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { LeafData, SiblingPath } from '@aztec/types'; +import { Hasher, IndexedTreeLeaf, IndexedTreeLeafPreimage, SiblingPath } from '@aztec/types'; -import { BatchInsertionResult, IndexedTree } from '../interfaces/indexed_tree.js'; +import { LevelUp } from 'levelup'; + +import { BatchInsertionResult, IndexedTree, LowLeafWitnessData } from '../index.js'; import { TreeBase } from '../tree_base.js'; const log = createDebugLogger('aztec:standard-indexed-tree'); -const indexToKeyLeaf = (name: string, index: bigint) => { - return `${name}:leaf:${toBufferBE(index, 32).toString('hex')}`; -}; - -const keyLeafToIndex = (key: string): bigint => { - const index = key.split(':')[2]; - return toBigIntBE(Buffer.from(index, 'hex')); -}; +/* eslint-disable */ -const zeroLeaf: LeafData = { - value: 0n, - nextValue: 0n, - nextIndex: 0n, -}; - -/** - * All of the data to be return during batch insertion. - */ -export interface LowLeafWitnessData { - /** - * Preimage of the low nullifier that proves non membership. - */ - leafData: LeafData; - /** - * Sibling path to prove membership of low nullifier. - */ - siblingPath: SiblingPath; - /** - * The index of low nullifier. - */ - index: bigint; -} +// TODO /** * Pre-compute empty witness. * @param treeHeight - Height of tree for sibling path. * @returns An empty witness. */ -function getEmptyLowLeafWitness(treeHeight: N): LowLeafWitnessData { +function getEmptyLowLeafWitness( + treeHeight: N, + preimageFactory: Empty>, +): LowLeafWitnessData { return { - leafData: zeroLeaf, + leafData: preimageFactory.empty(), index: 0n, siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), }; } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const encodeTreeValue = (leafData: LeafData) => { - const valueAsBuffer = toBufferBE(leafData.value, 32); - const indexAsBuffer = toBufferBE(leafData.nextIndex, 32); - const nextValueAsBuffer = toBufferBE(leafData.nextValue, 32); - return Buffer.concat([valueAsBuffer, indexAsBuffer, nextValueAsBuffer]); +interface Empty { + empty(): T; +} + +interface DummyBuilder { + buildDummy(key: bigint): T; +} + +interface FromBuffer { + fromBuffer(buffer: Buffer): T; +} + +interface PreimageFactory { + fromLeaf(leaf: T, nextKey: bigint, nextIndex: bigint): IndexedTreeLeafPreimage; +} + +const leafKeyToDbKey = (name: string, key: bigint) => { + return `${name}:leaf:${toBufferBE(key, 32).toString('hex')}`; }; -const decodeTreeValue = (buf: Buffer) => { - const value = toBigIntBE(buf.subarray(0, 32)); - const nextIndex = toBigIntBE(buf.subarray(32, 64)); - const nextValue = toBigIntBE(buf.subarray(64, 96)); - return { - value, - nextIndex, - nextValue, - } as LeafData; +const dbKeyToLeafKey = (key: string): bigint => { + const index = key.split(':')[2]; + return toBigIntBE(Buffer.from(index, 'hex')); }; -/** - * Indexed merkle tree. - */ -export class StandardIndexedTree extends TreeBase implements IndexedTree { - protected leaves: LeafData[] = []; - protected cachedLeaves: { [key: number]: LeafData } = {}; +export class StandardIndexedTree extends TreeBase implements IndexedTree { + protected leafPreimages: IndexedTreeLeafPreimage[] = []; + protected cachedLeafPreimages: { [key: number]: IndexedTreeLeafPreimage } = {}; + + public constructor( + db: LevelUp, + hasher: Hasher, + name: string, + depth: number, + size: bigint = 0n, + protected leafPreimageFactory: FromBuffer> & + Empty> & + PreimageFactory, + protected leafFactory: FromBuffer & DummyBuilder, + root?: Buffer, + ) { + super(db, hasher, name, depth, size, root); + } /** - * Appends the given leaves to the tree. - * @param _leaves - The leaves to append. - * @returns Empty promise. - * @remarks Use batchInsert method instead. + * Appends a set of leaf values to the tree. + * @param leaves - The set of leaves to be appended. */ - public appendLeaves(_leaves: Buffer[]): Promise { + appendLeaves(leaves: Buffer[]): Promise { throw new Error('Not implemented'); } @@ -115,11 +106,11 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @returns The value of the leaf at the given index or undefined if the leaf is empty. */ public getLeafValue(index: bigint, includeUncommitted: boolean): Promise { - const leaf = this.getLatestLeafDataCopy(Number(index), includeUncommitted); + const leaf = this.getLatestLeafPreimageCopy(Number(index), includeUncommitted); if (!leaf) { return Promise.resolve(undefined); } - return Promise.resolve(toBufferBE(leaf.value, 32)); + return Promise.resolve(leaf.toBuffer()); } /** @@ -128,7 +119,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`. */ - findIndexOfPreviousValue( + findIndexOfPreviousKey( newValue: bigint, includeUncommitted: boolean, ): { @@ -145,39 +136,38 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const diff: bigint[] = []; for (let i = 0; i < numLeaves; i++) { - const storedLeaf = this.getLatestLeafDataCopy(i, includeUncommitted)!; + const storedPreimage = this.getLatestLeafPreimageCopy(i, includeUncommitted); // The stored leaf can be undefined if it addresses an empty leaf // If the leaf is empty we do the same as if the leaf was larger - if (storedLeaf === undefined) { + if (storedPreimage === undefined) { diff.push(newValue); - } else if (storedLeaf.value > newValue) { + } else if (storedPreimage.key > newValue) { diff.push(newValue); - } else if (storedLeaf.value === newValue) { + } else if (storedPreimage.key === newValue) { return { index: i, alreadyPresent: true }; } else { - diff.push(newValue - storedLeaf.value); + diff.push(newValue - storedPreimage.key); } } - const minIndex = this.findMinIndex(diff); + const minIndex = this.findMinKey(diff); return { index: minIndex, alreadyPresent: false }; } /** - * Gets the latest LeafData copy. - * @param index - Index of the leaf of which to obtain the LeafData copy. + * Gets the latest LeafPreimage copy. + * @param index - Index of the leaf of which to obtain the LeafPreimage copy. * @param includeUncommitted - If true, the uncommitted changes are included in the search. - * @returns A copy of the leaf data at the given index or undefined if the leaf was not found. + * @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found. */ - public getLatestLeafDataCopy(index: number, includeUncommitted: boolean): LeafData | undefined { - const leaf = !includeUncommitted ? this.leaves[index] : this.cachedLeaves[index] ?? this.leaves[index]; - return leaf - ? ({ - value: leaf.value, - nextIndex: leaf.nextIndex, - nextValue: leaf.nextValue, - } as LeafData) - : undefined; + public getLatestLeafPreimageCopy( + index: number, + includeUncommitted: boolean, + ): IndexedTreeLeafPreimage | undefined { + const preimage = !includeUncommitted + ? this.leafPreimages[index] + : this.cachedLeafPreimages[index] ?? this.leafPreimages[index]; + return preimage?.clone(); } /** @@ -185,7 +175,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param values - The collection of values to be searched. * @returns The index of the minimum value in the array. */ - private findMinIndex(values: bigint[]) { + private findMinKey(values: bigint[]) { if (!values.length) { return 0; } @@ -217,22 +207,19 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { throw new Error(`Prefilled size must be at least 1!`); } - const leaves: LeafData[] = []; + const leaves: IndexedTreeLeafPreimage[] = []; for (let i = 0n; i < prefilledSize; i++) { - const newLeaf = { - value: toBigIntBE(Buffer.from([Number(i)])), - nextIndex: i + 1n, - nextValue: i + 1n, - }; - leaves.push(newLeaf); + const newLeaf = this.leafFactory.buildDummy(toBigIntBE(Buffer.from([Number(i)]))); + const newLeafPreimage = this.leafPreimageFactory.fromLeaf(newLeaf, i + 1n, i + 1n); + leaves.push(newLeafPreimage); } - // Make the first leaf have 0 value - leaves[0].value = 0n; + // Make the first leaf have 0 key + leaves[0].key = 0n; // Make the last leaf point to the first leaf leaves[prefilledSize - 1].nextIndex = 0n; - leaves[prefilledSize - 1].nextValue = 0n; + leaves[prefilledSize - 1].nextKey = 0n; await this.encodeAndAppendLeaves(leaves, true); await this.commit(); @@ -243,16 +230,16 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { */ public async initFromDb(): Promise { const startingIndex = 0n; - const values: LeafData[] = []; + const preimages: IndexedTreeLeafPreimage[] = []; const promise = new Promise((resolve, reject) => { this.db .createReadStream({ - gte: indexToKeyLeaf(this.getName(), startingIndex), - lte: indexToKeyLeaf(this.getName(), 2n ** BigInt(this.getDepth())), + gte: leafKeyToDbKey(this.getName(), startingIndex), + lte: leafKeyToDbKey(this.getName(), 2n ** BigInt(this.getDepth())), }) - .on('data', function (data) { - const index = keyLeafToIndex(data.key.toString('utf-8')); - values[Number(index)] = decodeTreeValue(data.value); + .on('data', data => { + const leafKey = dbKeyToLeafKey(data.key.toString('utf-8')); + preimages[Number(leafKey)] = this.leafPreimageFactory.fromBuffer(data.value); }) .on('close', function () {}) .on('end', function () { @@ -264,7 +251,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { }); }); await promise; - this.leaves = values; + this.leafPreimages = preimages; } /** @@ -272,11 +259,11 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { */ private async commitLeaves(): Promise { const batch = this.db.batch(); - const keys = Object.getOwnPropertyNames(this.cachedLeaves); + const keys = Object.getOwnPropertyNames(this.cachedLeafPreimages); for (const key of keys) { const index = Number(key); - batch.put(indexToKeyLeaf(this.getName(), BigInt(index)), encodeTreeValue(this.cachedLeaves[index])); - this.leaves[index] = this.cachedLeaves[index]; + batch.put(leafKeyToDbKey(this.getName(), BigInt(index)), this.cachedLeafPreimages[index].toBuffer()); + this.leafPreimages[index] = this.cachedLeafPreimages[index]; } await batch.write(); this.clearCachedLeaves(); @@ -286,20 +273,20 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * Clears the cache. */ private clearCachedLeaves() { - this.cachedLeaves = {}; + this.cachedLeafPreimages = {}; } /** * Updates a leaf in the tree. - * @param leaf - New contents of the leaf. + * @param preimage - New contents of the leaf. * @param index - Index of the leaf to be updated. */ - protected async updateLeaf(leaf: LeafData, index: bigint) { + protected async updateLeaf(preimage: IndexedTreeLeafPreimage, index: bigint) { if (index > this.maxIndex) { throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`); } - const encodedLeaf = this.encodeLeaf(leaf, true); + const encodedLeaf = this.encodeLeaf(preimage, true); await this.addLeafToCacheAndHashToRoot(encodedLeaf, index); const numLeaves = this.getNumLeaves(true); if (index >= numLeaves) { @@ -422,46 +409,46 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { >( leaves: Buffer[], subtreeHeight: SubtreeHeight, - ): Promise> { - const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight); + ): Promise> { + const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); // Accumulators - const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); - const pendingInsertionSubtree: LeafData[] = leaves.map(() => zeroLeaf); + const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); + const pendingInsertionSubtree: IndexedTreeLeafPreimage[] = leaves.map(() => this.leafPreimageFactory.empty()); // Start info const startInsertionIndex = this.getNumLeaves(true); - const leavesToInsert = leaves.map(leaf => toBigIntBE(leaf)); + const leavesToInsert = leaves.map(leaf => this.leafFactory.fromBuffer(leaf)); const sortedDescendingLeafTuples = leavesToInsert .map((leaf, index) => ({ leaf, index })) - .sort((a, b) => Number(b.leaf - a.leaf)); + .sort((a, b) => Number(b.leaf.getKey() - a.leaf.getKey())); const sortedDescendingLeaves = sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf); // Get insertion path for each leaf for (let i = 0; i < leavesToInsert.length; i++) { - const newValue = sortedDescendingLeaves[i]; - const originalIndex = leavesToInsert.indexOf(newValue); + const newLeaf = sortedDescendingLeaves[i]; + const originalIndex = leavesToInsert.indexOf(newLeaf); - if (newValue === 0n) { + if (newLeaf.isEmpty()) { continue; } - const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true); + const indexOfPrevious = this.findIndexOfPreviousKey(newLeaf.getKey(), true); // get the low leaf - const lowLeaf = this.getLatestLeafDataCopy(indexOfPrevious.index, true); - if (lowLeaf === undefined) { + const lowLeafPreimage = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true); + if (lowLeafPreimage === undefined) { return { lowLeavesWitnessData: undefined, - sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => new Fr(leafTuple.leaf).toBuffer()), + sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf.toBuffer()), sortedNewLeavesIndexes: sortedDescendingLeafTuples.map(leafTuple => leafTuple.index), newSubtreeSiblingPath: await this.getSubtreeSiblingPath(subtreeHeight, true), }; } const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); - const witness: LowLeafWitnessData = { - leafData: { ...lowLeaf }, + const witness: LowLeafWitnessData = { + leafData: lowLeafPreimage.clone(), index: BigInt(indexOfPrevious.index), siblingPath, }; @@ -469,20 +456,20 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { // Update the running paths lowLeavesWitnesses[i] = witness; - const currentPendingLeaf: LeafData = { - value: newValue, - nextValue: lowLeaf.nextValue, - nextIndex: lowLeaf.nextIndex, - }; + const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf( + newLeaf, + lowLeafPreimage.nextKey, + lowLeafPreimage.nextIndex, + ); - pendingInsertionSubtree[originalIndex] = currentPendingLeaf; + pendingInsertionSubtree[originalIndex] = currentPendingPreimageLeaf; - lowLeaf.nextValue = newValue; - lowLeaf.nextIndex = startInsertionIndex + BigInt(originalIndex); + lowLeafPreimage.nextKey = newLeaf.getKey(); + lowLeafPreimage.nextIndex = startInsertionIndex + BigInt(originalIndex); const lowLeafIndex = indexOfPrevious.index; - this.cachedLeaves[lowLeafIndex] = lowLeaf; - await this.updateLeaf(lowLeaf, BigInt(lowLeafIndex)); + this.cachedLeafPreimages[lowLeafIndex] = lowLeafPreimage; + await this.updateLeaf(lowLeafPreimage, BigInt(lowLeafIndex)); } const newSubtreeSiblingPath = await this.getSubtreeSiblingPath( @@ -497,7 +484,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { return { lowLeavesWitnessData: lowLeavesWitnesses, - sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => Buffer.from(new Fr(leafTuple.leaf).toBuffer())), + sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf.toBuffer()), sortedNewLeavesIndexes: sortedDescendingLeafTuples.map(leafTuple => leafTuple.index), newSubtreeSiblingPath, }; @@ -516,19 +503,19 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { /** * Encodes leaves and appends them to a tree. - * @param leaves - Leaves to encode. + * @param preimages - Leaves to encode. * @param hash0Leaf - Indicates whether 0 value leaf should be hashed. See {@link encodeLeaf}. * @returns Empty promise */ - private async encodeAndAppendLeaves(leaves: LeafData[], hash0Leaf: boolean): Promise { + private async encodeAndAppendLeaves(preimages: IndexedTreeLeafPreimage[], hash0Leaf: boolean): Promise { const startInsertionIndex = Number(this.getNumLeaves(true)); - const serializedLeaves = leaves.map((leaf, i) => { - this.cachedLeaves[startInsertionIndex + i] = leaf; - return this.encodeLeaf(leaf, hash0Leaf); + const hashedLeaves = preimages.map((preimage, i) => { + this.cachedLeafPreimages[startInsertionIndex + i] = preimage; + return this.encodeLeaf(preimage, hash0Leaf); }); - await super.appendLeaves(serializedLeaves); + await super.appendLeaves(hashedLeaves); } /** @@ -539,14 +526,12 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * nullifier it is improbable that a valid nullifier would be 0. * @returns Leaf encoded in a buffer. */ - private encodeLeaf(leaf: LeafData, hash0Leaf: boolean): Buffer { + private encodeLeaf(leaf: IndexedTreeLeafPreimage, hash0Leaf: boolean): Buffer { let encodedLeaf; - if (!hash0Leaf && leaf.value == 0n) { + if (!hash0Leaf && leaf.key == 0n) { encodedLeaf = toBufferBE(0n, 32); } else { - encodedLeaf = this.hasher.hashInputs( - [leaf.value, leaf.nextIndex, leaf.nextValue].map(val => toBufferBE(val, 32)), - ); + encodedLeaf = this.hasher.hashInputs(leaf.toHashInputs()); } return encodedLeaf; } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index 2f6db0b1ac14..b9154eb7dc6f 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -1,5 +1,5 @@ import { toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { Hasher, SiblingPath } from '@aztec/types'; +import { Hasher, NullifierLeaf, NullifierLeafPreimage, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; @@ -9,15 +9,40 @@ import { createMemDown } from '../../test/utils/create_mem_down.js'; import { StandardIndexedTreeWithAppend } from './standard_indexed_tree_with_append.js'; const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => { - return await newTree(StandardIndexedTreeWithAppend, levelUp, hasher, name, depth, prefilledSize); + return await newTree( + (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { + return new StandardIndexedTreeWithAppend(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf); + }, + levelUp, + hasher, + name, + depth, + prefilledSize, + ); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(StandardIndexedTreeWithAppend, levelUp, hasher, name); + return await loadTree( + (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => { + return new StandardIndexedTreeWithAppend( + db, + hasher, + name, + depth, + size, + NullifierLeafPreimage, + NullifierLeaf, + root, + ); + }, + levelUp, + hasher, + name, + ); }; const createIndexedTreeLeaf = (value: number, nextIndex: number, nextValue: number) => { - return [toBufferBE(BigInt(value), 32), toBufferBE(BigInt(nextIndex), 32), toBufferBE(BigInt(nextValue), 32)]; + return new NullifierLeafPreimage(BigInt(value), BigInt(nextValue), BigInt(nextIndex)).toHashInputs(); }; const verifyCommittedState = async ( diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 49a90e611f11..992cc31f112a 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -1,5 +1,4 @@ -import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; -import { LeafData } from '@aztec/types'; +import { IndexedTreeLeaf } from '@aztec/types'; import { StandardIndexedTree } from '../../index.js'; @@ -8,7 +7,7 @@ import { StandardIndexedTree } from '../../index.js'; * that was replaced by the more efficient batchInsert method. We keep the original implementation around as it useful * for testing that the more complex batchInsert method works correctly. */ -export class StandardIndexedTreeWithAppend extends StandardIndexedTree { +export class StandardIndexedTreeWithAppend extends StandardIndexedTree { /** * Appends the given leaves to the tree. * @param leaves - The leaves to append. @@ -27,10 +26,10 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { * @returns Empty promise. */ private async appendLeaf(leaf: Buffer): Promise { - const newValue = toBigIntBE(leaf); + const newLeaf = this.leafFactory.fromBuffer(leaf); // Special case when appending zero - if (newValue === 0n) { + if (newLeaf.getKey() === 0n) { const newSize = (this.cachedSize ?? this.size) + 1n; if (newSize - 1n > this.maxIndex) { throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`); @@ -39,27 +38,28 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { return; } - const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true); - const previousLeafCopy = this.getLatestLeafDataCopy(indexOfPrevious.index, true); + const indexOfPrevious = this.findIndexOfPreviousKey(newLeaf.getKey(), true); + const previousLeafCopy = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true); if (previousLeafCopy === undefined) { throw new Error(`Previous leaf not found!`); } - const newLeaf = { - value: newValue, - nextIndex: previousLeafCopy.nextIndex, - nextValue: previousLeafCopy.nextValue, - } as LeafData; + const newLeafPreimage = this.leafPreimageFactory.fromLeaf( + newLeaf, + previousLeafCopy.nextKey, + previousLeafCopy.nextIndex, + ); + if (indexOfPrevious.alreadyPresent) { return; } // insert a new leaf at the highest index and update the values of our previous leaf copy const currentSize = this.getNumLeaves(true); previousLeafCopy.nextIndex = BigInt(currentSize); - previousLeafCopy.nextValue = newLeaf.value; - this.cachedLeaves[Number(currentSize)] = newLeaf; - this.cachedLeaves[Number(indexOfPrevious.index)] = previousLeafCopy; + previousLeafCopy.nextKey = newLeaf.getKey(); + this.cachedLeafPreimages[Number(currentSize)] = newLeafPreimage; + this.cachedLeafPreimages[Number(indexOfPrevious.index)] = previousLeafCopy; await this.updateLeaf(previousLeafCopy, BigInt(indexOfPrevious.index)); - await this.updateLeaf(newLeaf, this.getNumLeaves(true)); + await this.updateLeaf(newLeafPreimage, this.getNumLeaves(true)); } } diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts index ee3191f42ffd..db67453c4bc8 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts @@ -4,7 +4,7 @@ import { Hasher } from '@aztec/types'; import { default as levelup } from 'levelup'; import { loadTree } from '../load_tree.js'; -import { newTree } from '../new_tree.js'; +import { builder, newTree } from '../new_tree.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; import { createMemDown } from '../test/utils/create_mem_down.js'; @@ -13,11 +13,11 @@ import { INITIAL_LEAF } from '../tree_base.js'; import { StandardTree } from './standard_tree.js'; const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => { - return await newTree(StandardTree, levelUp, hasher, name, depth); + return await newTree(builder(StandardTree), levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(StandardTree, levelUp, hasher, name); + return await loadTree(builder(StandardTree), levelUp, hasher, name); }; treeTestSuite('StandardTree', createDb, createFromName); diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr index 65cf52e96b5b..1bb62e9f3f6f 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr @@ -1,5 +1,5 @@ /* Autogenerated file, do not edit! */ - + use dep::std; use dep::aztec::context::{ PrivateContext, PublicContext }; use dep::aztec::constants_gen::RETURN_VALUES_LENGTH; @@ -26,6 +26,7 @@ struct ManyNotesADeepStructTestCodeGenStruct { secret_hash: Field, } + // Interface for calling Test functions from a private context struct TestPrivateContextInterface { address: Field, @@ -241,6 +242,9 @@ impl TestPrivateContextInterface { } } + + + // Interface for calling Test functions from a public context struct TestPublicContextInterface { @@ -326,4 +330,5 @@ impl TestPublicContextInterface { } } - + + diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index f351287cab22..9832cc337ffa 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -6,5 +6,5 @@ export * from './deployed-contract.js'; export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; -export * from './leaf_data.js'; -export * from './nullifier_witness.js'; +export * from './indexed_tree.js'; +export * from './nullifier_tree.js'; diff --git a/yarn-project/types/src/interfaces/indexed_tree.ts b/yarn-project/types/src/interfaces/indexed_tree.ts new file mode 100644 index 000000000000..d2a63e0dbd27 --- /dev/null +++ b/yarn-project/types/src/interfaces/indexed_tree.ts @@ -0,0 +1,36 @@ +/** + * A leaf of a tree. + */ +export interface LeafData { + /** + * A value of the leaf. + */ + value: bigint; + /** + * An index of the next leaf. + */ + nextIndex: bigint; + /** + * A value of the next leaf. + */ + nextValue: bigint; +} + +/* eslint-disable */ + +export interface IndexedTreeLeaf { + getKey(): bigint; + toBuffer(): Buffer; + isEmpty(): boolean; +} + +export interface IndexedTreeLeafPreimage { + key: bigint; + nextKey: bigint; + nextIndex: bigint; + + asLeaf(): Leaf; + toBuffer(): Buffer; + toHashInputs(): Buffer[]; + clone(): IndexedTreeLeafPreimage; +} diff --git a/yarn-project/types/src/interfaces/leaf_data.ts b/yarn-project/types/src/interfaces/leaf_data.ts deleted file mode 100644 index 2edc8e09818e..000000000000 --- a/yarn-project/types/src/interfaces/leaf_data.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * A leaf of a tree. - */ -export interface LeafData { - /** - * A value of the leaf. - */ - value: bigint; - /** - * An index of the next leaf. - */ - nextIndex: bigint; - /** - * A value of the next leaf. - */ - nextValue: bigint; -} diff --git a/yarn-project/types/src/interfaces/nullifier_tree.ts b/yarn-project/types/src/interfaces/nullifier_tree.ts new file mode 100644 index 000000000000..180472a988e0 --- /dev/null +++ b/yarn-project/types/src/interfaces/nullifier_tree.ts @@ -0,0 +1,107 @@ +import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; +import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; + +import { SiblingPath } from '../sibling_path.js'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage, LeafData } from './indexed_tree.js'; + +/** + * Nullifier membership witness. + * @remarks When this represents membership witness of a low nullifier it can be used to perform a nullifier + * non-inclusion proof by leveraging the "linked list structure" of leaves and proving that a lower nullifier + * is pointing to a bigger next value than the nullifier we are trying to prove non-inclusion for. + */ +export class NullifierMembershipWitness { + constructor( + /** + * The index of the nullifier in a tree. + */ + public readonly index: bigint, + /** + * Preimage of the nullifier. + */ + public readonly leafData: LeafData, + /** + * Sibling path to prove membership of the nullifier. + */ + public readonly siblingPath: SiblingPath, + ) {} + + /** + * Returns a field array representation of a nullifier witness. + * @returns A field array representation of a nullifier witness. + */ + public toFieldArray(): Fr[] { + return [ + new Fr(this.index), + new Fr(this.leafData.value), + new Fr(this.leafData.nextIndex), + new Fr(this.leafData.nextValue), + ...this.siblingPath.toFieldArray(), + ]; + } +} + +/* eslint-disable */ + +export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { + constructor(public key: bigint, public nextKey: bigint, public nextIndex: bigint) {} + + asLeaf(): NullifierLeaf { + return new NullifierLeaf(new Fr(this.key)); + } + + toBuffer(): Buffer { + return Buffer.concat(this.toHashInputs()); + } + + toHashInputs(): Buffer[] { + return [ + Buffer.from(toBufferBE(this.key, 32)), + Buffer.from(toBufferBE(this.nextIndex, 32)), + Buffer.from(toBufferBE(this.nextKey, 32)), + ]; + } + + clone(): NullifierLeafPreimage { + return new NullifierLeafPreimage(this.key, this.nextKey, this.nextIndex); + } + + static empty(): NullifierLeafPreimage { + return new NullifierLeafPreimage(0n, 0n, 0n); + } + + static fromBuffer(buf: Buffer): NullifierLeafPreimage { + const key = toBigIntBE(buf.subarray(0, 32)); + const nextKey = toBigIntBE(buf.subarray(32, 64)); + const nextIndex = toBigIntBE(buf.subarray(64, 96)); + return new NullifierLeafPreimage(key, nextKey, nextIndex); + } + + static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { + return new NullifierLeafPreimage(leaf.nullifier.toBigInt(), nextKey, nextIndex); + } +} + +export class NullifierLeaf implements IndexedTreeLeaf { + constructor(public nullifier: Fr) {} + + getKey(): bigint { + return this.nullifier.toBigInt(); + } + + toBuffer(): Buffer { + return this.nullifier.toBuffer(); + } + + isEmpty(): boolean { + return this.nullifier.isZero(); + } + + static buildDummy(key: bigint): NullifierLeaf { + return new NullifierLeaf(new Fr(key)); + } + + static fromBuffer(buf: Buffer): NullifierLeaf { + return new NullifierLeaf(Fr.fromBuffer(buf)); + } +} diff --git a/yarn-project/types/src/interfaces/nullifier_witness.ts b/yarn-project/types/src/interfaces/nullifier_witness.ts deleted file mode 100644 index 90dc6d9a1c7c..000000000000 --- a/yarn-project/types/src/interfaces/nullifier_witness.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; - -import { SiblingPath } from '../sibling_path.js'; -import { LeafData } from './leaf_data.js'; - -/** - * Nullifier membership witness. - * @remarks When this represents membership witness of a low nullifier it can be used to perform a nullifier - * non-inclusion proof by leveraging the "linked list structure" of leaves and proving that a lower nullifier - * is pointing to a bigger next value than the nullifier we are trying to prove non-inclusion for. - */ -export class NullifierMembershipWitness { - constructor( - /** - * The index of the nullifier in a tree. - */ - public readonly index: bigint, - /** - * Preimage of the nullifier. - */ - public readonly leafData: LeafData, - /** - * Sibling path to prove membership of the nullifier. - */ - public readonly siblingPath: SiblingPath, - ) {} - - /** - * Returns a field array representation of a nullifier witness. - * @returns A field array representation of a nullifier witness. - */ - public toFieldArray(): Fr[] { - return [ - new Fr(this.index), - new Fr(this.leafData.value), - new Fr(this.leafData.nextIndex), - new Fr(this.leafData.nextValue), - ...this.siblingPath.toFieldArray(), - ]; - } -} diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 68d01812a7e6..75690f6a5644 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -12,7 +12,7 @@ import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; -import { NullifierMembershipWitness } from './nullifier_witness.js'; +import { NullifierMembershipWitness } from './nullifier_tree.js'; /** * Interface providing methods for retrieving information about content of the state trees. diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 312403577483..5725ec141fe2 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,6 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeDb, MerkleTreeOperations, TreeInfo } from '../index.js'; @@ -89,8 +89,12 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * @param index - The index of the leaf to get. * @returns Leaf data. */ - getLeafData(treeId: MerkleTreeId.NULLIFIER_TREE, index: number): Promise { - return this.trees.getLeafData(treeId, index, this.includeUncommitted); + async getLeafPreimage( + treeId: MerkleTreeId.NULLIFIER_TREE, + index: number, + ): Promise | undefined> { + const preimage = await this.trees.getLeafPreimage(treeId, index, this.includeUncommitted); + return preimage as IndexedTreeLeafPreimage | undefined; } /** @@ -171,11 +175,11 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * @param subtreeHeight - Height of the subtree. * @returns The data for the leaves to be updated when inserting the new ones. */ - public batchInsert( + public batchInsert( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: number, - ): Promise> { + ): Promise> { return this.trees.batchInsert(treeId, leaves, subtreeHeight); } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 8f191517a82d..40be71456aae 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -2,7 +2,7 @@ import { MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; /** * Type alias for the nullifier tree ID. @@ -146,7 +146,10 @@ export interface MerkleTreeOperations { * @param treeId - The tree for which leaf data should be returned. * @param index - The index of the leaf required. */ - getLeafData(treeId: IndexedTreeId, index: number): Promise; + getLeafPreimage( + treeId: IndexedTreeId, + index: number, + ): Promise | undefined>; /** * Update the leaf data at the given index. @@ -195,11 +198,11 @@ export interface MerkleTreeOperations { * @param subtreeHeight - Height of the subtree. * @returns The witness data for the leaves to be updated when inserting the new ones. */ - batchInsert( + batchInsert( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: number, - ): Promise>; + ): Promise>; /** * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index a27b1706612f..577705dac81f 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -25,7 +25,7 @@ import { loadTree, newTree, } from '@aztec/merkle-tree'; -import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; @@ -321,7 +321,7 @@ export class MerkleTrees implements MerkleTreeDb { alreadyPresent: boolean; }> { return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).findIndexOfPreviousValue(value, includeUncommitted)), + Promise.resolve(this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)), ); } @@ -332,13 +332,13 @@ export class MerkleTrees implements MerkleTreeDb { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns Leaf data. */ - public async getLeafData( + public async getLeafPreimage( treeId: IndexedTreeId, index: number, includeUncommitted: boolean, - ): Promise { + ): Promise | undefined> { return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).getLatestLeafDataCopy(index, includeUncommitted)), + Promise.resolve(this._getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted)), ); } @@ -397,12 +397,13 @@ export class MerkleTrees implements MerkleTreeDb { TreeHeight extends number, SubtreeHeight extends number, SubtreeSiblingPathHeight extends number, + Leaf extends IndexedTreeLeaf, >( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: SubtreeHeight, - ): Promise> { - const tree = this.trees[treeId] as StandardIndexedTree; + ): Promise> { + const tree = this.trees[treeId] as StandardIndexedTree; if (!('batchInsert' in tree)) { throw new Error('Tree does not support `batchInsert` method'); } @@ -453,8 +454,8 @@ export class MerkleTrees implements MerkleTreeDb { * @param treeId - Id of the tree to get an instance of. * @returns The indexed tree for the specified tree id. */ - private _getIndexedTree(treeId: IndexedTreeId): IndexedTree { - return this.trees[treeId] as IndexedTree; + private _getIndexedTree(treeId: IndexedTreeId): IndexedTree { + return this.trees[treeId] as IndexedTree; } /** From ed471d49c25def39a6d835ebe9b4b2140df9b101 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 12:25:44 +0000 Subject: [PATCH 02/40] feat: experiment with generic indexed trees --- .../src/client/private_execution.test.ts | 4 +- .../aztec-node/src/aztec-node/server.ts | 12 +- .../src/structs/rollup/base_rollup.ts | 94 ++++++++++++-- .../circuits.js/src/tests/factories.ts | 2 +- .../src/e2e_blacklist_token_contract.test.ts | 10 +- .../end-to-end/src/e2e_slow_tree.test.ts | 10 +- yarn-project/foundation/package.json | 1 + yarn-project/foundation/src/index.ts | 1 + yarn-project/foundation/src/trees/index.ts | 17 +++ yarn-project/merkle-tree/src/index.ts | 2 +- .../src/interfaces/indexed_tree.ts | 19 ++- .../standard_indexed_tree.ts | 119 +++++++++--------- .../test/standard_indexed_tree.test.ts | 43 +++---- .../test/standard_indexed_tree_with_append.ts | 26 ++-- .../src/type_conversion.ts | 4 +- .../src/block_builder/solo_block_builder.ts | 28 ++--- .../types/src/interfaces/indexed_tree.ts | 19 --- .../types/src/interfaces/nullifier_tree.ts | 75 +---------- .../merkle_tree_operations_facade.ts | 18 ++- .../src/world-state-db/merkle_tree_db.ts | 16 ++- .../src/world-state-db/merkle_trees.ts | 41 +++--- 21 files changed, 315 insertions(+), 246 deletions(-) create mode 100644 yarn-project/foundation/src/trees/index.ts diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index b96d69dc3384..0635036183fa 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -27,7 +27,7 @@ import { pedersenHash } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { AppendOnlyTree, Pedersen, StandardTree, newTree } from '@aztec/merkle-tree'; +import { AppendOnlyTree, Pedersen, StandardTree, newTree, treeBuilder } from '@aztec/merkle-tree'; import { ChildContractArtifact, ImportTestContractArtifact, @@ -126,7 +126,7 @@ describe('Private Execution test suite', () => { if (!trees[name]) { const db = levelup(createMemDown()); const pedersen = new Pedersen(); - trees[name] = await newTree(StandardTree, db, pedersen, name, treeHeights[name]); + trees[name] = await newTree(treeBuilder(StandardTree), db, pedersen, name, treeHeights[name]); } await trees[name].appendLeaves(leaves.map(l => l.toBuffer())); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 817b1a6b2067..a197bbfccd17 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -8,6 +8,8 @@ import { L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + NullifierLeaf, + NullifierLeafPreimage, PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computeGlobalsHash, computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; @@ -401,7 +403,10 @@ export class AztecNodeService implements AztecNode { return undefined; } - const leafDataPromise = committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, Number(index)); + const leafDataPromise = committedDb.getLeafPreimage( + MerkleTreeId.NULLIFIER_TREE, + Number(index), + ); const siblingPathPromise = committedDb.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), @@ -442,7 +447,10 @@ export class AztecNodeService implements AztecNode { if (alreadyPresent) { this.log.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); } - const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, index); + const leafData = await committedDb.getLeafPreimage( + MerkleTreeId.NULLIFIER_TREE, + index, + ); if (!leafData) { return undefined; } diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts index 454532ec6467..894f3ec0d933 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts @@ -1,5 +1,8 @@ +import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, Tuple } from '@aztec/foundation/serialize'; +import { IndexedTreeLeaf } from '@aztec/foundation/trees'; +import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, @@ -25,28 +28,103 @@ import { AppendOnlyTreeSnapshot } from './append_only_tree_snapshot.js'; * Class containing the data of a preimage of a single leaf in the nullifier tree. * Note: It's called preimage because this data gets hashed before being inserted as a node into the `IndexedTree`. */ -export class NullifierLeafPreimage { +export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { constructor( /** * Leaf value inside the indexed tree's linked list. */ - public leafValue: Fr, + public nullifier: Fr, /** * Next value inside the indexed tree's linked list. */ - public nextValue: Fr, + public nextNullifier: Fr, /** * Index of the next leaf in the indexed tree's linked list. */ - public nextIndex: UInt32, + public nextIndex: bigint, ) {} - toBuffer() { - return serializeToBuffer(this.leafValue, this.nextValue, this.nextIndex); + getKey(): bigint { + return this.nullifier.toBigInt(); + } + + getNextKey(): bigint { + return this.nextNullifier.toBigInt(); + } + + getNextIndex(): bigint { + return this.nextIndex; + } + + asLeaf(): NullifierLeaf { + return new NullifierLeaf(this.nullifier); + } + + toBuffer(): Buffer { + return Buffer.concat(this.toHashInputs()); + } + + toHashInputs(): Buffer[] { + return [ + Buffer.from(this.nullifier.toBuffer()), + Buffer.from(toBufferBE(this.nextIndex, 32)), + Buffer.from(this.nextNullifier.toBuffer()), + ]; + } + + clone(): NullifierLeafPreimage { + return new NullifierLeafPreimage(this.nullifier, this.nextNullifier, this.nextIndex); + } + + static empty(): NullifierLeafPreimage { + return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0n); + } + + static fromBuffer(buf: Buffer): NullifierLeafPreimage { + const key = Fr.fromBuffer(buf.subarray(0, 32)); + const nextKey = Fr.fromBuffer(buf.subarray(32, 64)); + const nextIndex = toBigIntBE(buf.subarray(64, 96)); + return new NullifierLeafPreimage(key, nextKey, nextIndex); + } + + static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { + return new NullifierLeafPreimage(leaf.nullifier, new Fr(nextKey), nextIndex); + } + + static clone(preimage: NullifierLeafPreimage): NullifierLeafPreimage { + return new NullifierLeafPreimage(preimage.nullifier, preimage.nextNullifier, preimage.nextIndex); + } +} + +/** + * A nullifier to be inserted in the nullifier tree. + */ +export class NullifierLeaf implements IndexedTreeLeaf { + constructor( + /** + * Nullifier value. + */ + public nullifier: Fr, + ) {} + + getKey(): bigint { + return this.nullifier.toBigInt(); + } + + toBuffer(): Buffer { + return this.nullifier.toBuffer(); + } + + isEmpty(): boolean { + return this.nullifier.isZero(); + } + + static buildDummy(key: bigint): NullifierLeaf { + return new NullifierLeaf(new Fr(key)); } - static empty() { - return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0); + static fromBuffer(buf: Buffer): NullifierLeaf { + return new NullifierLeaf(Fr.fromBuffer(buf)); } } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 6c03169795a8..3d5acb065d4b 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -906,7 +906,7 @@ export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { const lowNullifierLeafPreimages = makeTuple( MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), x + 0x200), + x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), seed + 0x1000, ); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts index 1c1825c7d837..6a2232eee479 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts @@ -14,7 +14,7 @@ import { computeAuthWitMessageHash, computeMessageSecretHash, } from '@aztec/aztec.js'; -import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; +import { Pedersen, SparseTree, newTree, treeBuilder } from '@aztec/merkle-tree'; import { SlowTreeContract, TokenBlacklistContract, TokenContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; @@ -107,7 +107,13 @@ describe('e2e_blacklist_token_contract', () => { slowTree = await SlowTreeContract.deploy(wallets[0]).send().deployed(); const depth = 254; - slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); + slowUpdateTreeSimulator = await newTree( + treeBuilder(SparseTree), + levelup(createMemDown()), + new Pedersen(), + 'test', + depth, + ); const deployTx = TokenBlacklistContract.deploy(wallets[0], accounts[0], slowTree.address).send({}); const receipt = await deployTx.wait(); diff --git a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts index 87c267e5e823..e0d58be36ee7 100644 --- a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts +++ b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts @@ -1,6 +1,6 @@ /* eslint-disable camelcase */ import { CheatCodes, DebugLogger, Fr, Wallet } from '@aztec/aztec.js'; -import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; +import { Pedersen, SparseTree, newTree, treeBuilder } from '@aztec/merkle-tree'; import { SlowTreeContract } from '@aztec/noir-contracts/types'; import { default as levelup } from 'levelup'; @@ -27,7 +27,13 @@ describe('e2e_slow_tree', () => { it('Messing around with noir slow tree', async () => { const depth = 254; - const slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); + const slowUpdateTreeSimulator = await newTree( + treeBuilder(SparseTree), + levelup(createMemDown()), + new Pedersen(), + 'test', + depth, + ); const getMembershipProof = async (index: bigint, includeUncommitted: boolean) => { return { index, diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 98a10914965d..bb413f7e0d27 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -28,6 +28,7 @@ "./sleep": "./dest/sleep/index.js", "./timer": "./dest/timer/index.js", "./transport": "./dest/transport/index.js", + "./trees": "./dest/trees/index.js", "./wasm": "./dest/wasm/index.js", "./worker": "./dest/worker/index.js", "./bigint-buffer": "./dest/bigint-buffer/index.js", diff --git a/yarn-project/foundation/src/index.ts b/yarn-project/foundation/src/index.ts index b34a0bf474eb..300b417c2888 100644 --- a/yarn-project/foundation/src/index.ts +++ b/yarn-project/foundation/src/index.ts @@ -21,6 +21,7 @@ export * as serialize from './serialize/index.js'; export * as sleep from './sleep/index.js'; export * as timer from './timer/index.js'; export * as transport from './transport/index.js'; +export * as trees from './trees/index.js'; export * as types from './types/index.js'; export * as url from './url/index.js'; export * as wasm from './wasm/index.js'; diff --git a/yarn-project/foundation/src/trees/index.ts b/yarn-project/foundation/src/trees/index.ts new file mode 100644 index 000000000000..139b63836205 --- /dev/null +++ b/yarn-project/foundation/src/trees/index.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ + +export interface IndexedTreeLeaf { + getKey(): bigint; + toBuffer(): Buffer; + isEmpty(): boolean; +} + +export interface IndexedTreeLeafPreimage { + getKey(): bigint; + getNextKey(): bigint; + getNextIndex(): bigint; + + asLeaf(): Leaf; + toBuffer(): Buffer; + toHashInputs(): Buffer[]; +} diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index de19e295bfdf..ae9d417a40b5 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -7,5 +7,5 @@ export * from './sparse_tree/sparse_tree.js'; export * from './standard_indexed_tree/standard_indexed_tree.js'; export * from './standard_tree/standard_tree.js'; export { INITIAL_LEAF } from './tree_base.js'; -export { newTree } from './new_tree.js'; +export { newTree, builder as treeBuilder } from './new_tree.js'; export { loadTree } from './load_tree.js'; diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index 17c66a9100d5..25ee825aadc6 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -1,4 +1,5 @@ -import { IndexedTreeLeaf, IndexedTreeLeafPreimage, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { SiblingPath } from '@aztec/types'; import { AppendOnlyTree } from './append_only_tree.js'; @@ -7,11 +8,15 @@ import { AppendOnlyTree } from './append_only_tree.js'; /** * All of the data to be return during batch insertion. */ -export interface LowLeafWitnessData { +export interface LowLeafWitnessData< + N extends number, + Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, +> { /** * Preimage of the low nullifier that proves non membership. */ - leafData: IndexedTreeLeafPreimage; + leafData: Preimage; /** * Sibling path to prove membership of low nullifier. */ @@ -29,11 +34,12 @@ export interface BatchInsertionResult< TreeHeight extends number, SubtreeSiblingPathHeight extends number, Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, > { /** * Data for the leaves to be updated when inserting the new ones. */ - lowLeavesWitnessData?: LowLeafWitnessData[]; + lowLeavesWitnessData?: LowLeafWitnessData[]; /** * Sibling path "pointing to" where the new subtree should be inserted into the tree. */ @@ -51,7 +57,8 @@ export interface BatchInsertionResult< /** * Indexed merkle tree. */ -export interface IndexedTree extends AppendOnlyTree { +export interface IndexedTree> + extends AppendOnlyTree { /** * Finds the index of the largest leaf whose value is less than or equal to the provided value. * @param newValue - The new value to be inserted into the tree. @@ -90,5 +97,5 @@ export interface IndexedTree extends AppendOnlyTre leaves: Buffer[], subtreeHeight: SubtreeHeight, includeUncommitted: boolean, - ): Promise>; + ): Promise>; } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index b18e00275ae3..32c8944bcadb 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -1,6 +1,7 @@ import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; import { createDebugLogger } from '@aztec/foundation/log'; -import { Hasher, IndexedTreeLeaf, IndexedTreeLeafPreimage, SiblingPath } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { Hasher, SiblingPath } from '@aztec/types'; import { LevelUp } from 'levelup'; @@ -13,22 +14,6 @@ const log = createDebugLogger('aztec:standard-indexed-tree'); // TODO -/** - * Pre-compute empty witness. - * @param treeHeight - Height of tree for sibling path. - * @returns An empty witness. - */ -function getEmptyLowLeafWitness( - treeHeight: N, - preimageFactory: Empty>, -): LowLeafWitnessData { - return { - leafData: preimageFactory.empty(), - index: 0n, - siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), - }; -} - interface Empty { empty(): T; } @@ -41,8 +26,12 @@ interface FromBuffer { fromBuffer(buffer: Buffer): T; } -interface PreimageFactory { - fromLeaf(leaf: T, nextKey: bigint, nextIndex: bigint): IndexedTreeLeafPreimage; +interface Clone { + clone(t: T): T; +} + +interface PreimageFactory> { + fromLeaf(leaf: Leaf, nextKey: bigint, nextIndex: bigint): Preimage; } const leafKeyToDbKey = (name: string, key: bigint) => { @@ -54,9 +43,12 @@ const dbKeyToLeafKey = (key: string): bigint => { return toBigIntBE(Buffer.from(index, 'hex')); }; -export class StandardIndexedTree extends TreeBase implements IndexedTree { - protected leafPreimages: IndexedTreeLeafPreimage[] = []; - protected cachedLeafPreimages: { [key: number]: IndexedTreeLeafPreimage } = {}; +export class StandardIndexedTree> + extends TreeBase + implements IndexedTree +{ + protected leafPreimages: Preimage[] = []; + protected cachedLeafPreimages: { [key: number]: Preimage } = {}; public constructor( db: LevelUp, @@ -64,9 +56,10 @@ export class StandardIndexedTree extends TreeBase name: string, depth: number, size: bigint = 0n, - protected leafPreimageFactory: FromBuffer> & - Empty> & - PreimageFactory, + protected leafPreimageFactory: FromBuffer & + Empty & + PreimageFactory & + Clone, protected leafFactory: FromBuffer & DummyBuilder, root?: Buffer, ) { @@ -142,12 +135,12 @@ export class StandardIndexedTree extends TreeBase // If the leaf is empty we do the same as if the leaf was larger if (storedPreimage === undefined) { diff.push(newValue); - } else if (storedPreimage.key > newValue) { + } else if (storedPreimage.getKey() > newValue) { diff.push(newValue); - } else if (storedPreimage.key === newValue) { + } else if (storedPreimage.getKey() === newValue) { return { index: i, alreadyPresent: true }; } else { - diff.push(newValue - storedPreimage.key); + diff.push(newValue - storedPreimage.getKey()); } } const minIndex = this.findMinKey(diff); @@ -160,14 +153,11 @@ export class StandardIndexedTree extends TreeBase * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found. */ - public getLatestLeafPreimageCopy( - index: number, - includeUncommitted: boolean, - ): IndexedTreeLeafPreimage | undefined { + public getLatestLeafPreimageCopy(index: number, includeUncommitted: boolean): Preimage | undefined { const preimage = !includeUncommitted ? this.leafPreimages[index] : this.cachedLeafPreimages[index] ?? this.leafPreimages[index]; - return preimage?.clone(); + return this.leafPreimageFactory.clone(preimage); } /** @@ -207,19 +197,15 @@ export class StandardIndexedTree extends TreeBase throw new Error(`Prefilled size must be at least 1!`); } - const leaves: IndexedTreeLeafPreimage[] = []; + const leaves: Preimage[] = []; for (let i = 0n; i < prefilledSize; i++) { - const newLeaf = this.leafFactory.buildDummy(toBigIntBE(Buffer.from([Number(i)]))); + const newLeaf = this.leafFactory.buildDummy(i); const newLeafPreimage = this.leafPreimageFactory.fromLeaf(newLeaf, i + 1n, i + 1n); leaves.push(newLeafPreimage); } - // Make the first leaf have 0 key - leaves[0].key = 0n; - // Make the last leaf point to the first leaf - leaves[prefilledSize - 1].nextIndex = 0n; - leaves[prefilledSize - 1].nextKey = 0n; + leaves[prefilledSize - 1] = this.leafPreimageFactory.fromLeaf(leaves[prefilledSize - 1].asLeaf(), 0n, 0n); await this.encodeAndAppendLeaves(leaves, true); await this.commit(); @@ -230,7 +216,7 @@ export class StandardIndexedTree extends TreeBase */ public async initFromDb(): Promise { const startingIndex = 0n; - const preimages: IndexedTreeLeafPreimage[] = []; + const preimages: Preimage[] = []; const promise = new Promise((resolve, reject) => { this.db .createReadStream({ @@ -281,7 +267,7 @@ export class StandardIndexedTree extends TreeBase * @param preimage - New contents of the leaf. * @param index - Index of the leaf to be updated. */ - protected async updateLeaf(preimage: IndexedTreeLeafPreimage, index: bigint) { + protected async updateLeaf(preimage: Preimage, index: bigint) { if (index > this.maxIndex) { throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`); } @@ -294,6 +280,22 @@ export class StandardIndexedTree extends TreeBase } } + /** + * Pre-compute empty witness. + * @param treeHeight - Height of tree for sibling path. + * @returns An empty witness. + */ + getEmptyLowLeafWitnes( + treeHeight: N, + preimageFactory: Empty, + ): LowLeafWitnessData { + return { + leafData: preimageFactory.empty(), + index: 0n, + siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), + }; + } + /* eslint-disable jsdoc/require-description-complete-sentence */ /* The following doc block messes up with complete-sentence, so we just disable it */ @@ -409,11 +411,11 @@ export class StandardIndexedTree extends TreeBase >( leaves: Buffer[], subtreeHeight: SubtreeHeight, - ): Promise> { - const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); + ): Promise> { + const emptyLowLeafWitness = this.getEmptyLowLeafWitnes(this.getDepth() as TreeHeight, this.leafPreimageFactory); // Accumulators - const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); - const pendingInsertionSubtree: IndexedTreeLeafPreimage[] = leaves.map(() => this.leafPreimageFactory.empty()); + const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); + const pendingInsertionSubtree: Preimage[] = leaves.map(() => this.leafPreimageFactory.empty()); // Start info const startInsertionIndex = this.getNumLeaves(true); @@ -447,8 +449,8 @@ export class StandardIndexedTree extends TreeBase } const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); - const witness: LowLeafWitnessData = { - leafData: lowLeafPreimage.clone(), + const witness: LowLeafWitnessData = { + leafData: lowLeafPreimage, index: BigInt(indexOfPrevious.index), siblingPath, }; @@ -458,18 +460,21 @@ export class StandardIndexedTree extends TreeBase const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf( newLeaf, - lowLeafPreimage.nextKey, - lowLeafPreimage.nextIndex, + lowLeafPreimage.getNextKey(), + lowLeafPreimage.getNextIndex(), ); pendingInsertionSubtree[originalIndex] = currentPendingPreimageLeaf; - lowLeafPreimage.nextKey = newLeaf.getKey(); - lowLeafPreimage.nextIndex = startInsertionIndex + BigInt(originalIndex); + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + lowLeafPreimage.asLeaf(), + newLeaf.getKey(), + startInsertionIndex + BigInt(originalIndex), + ); const lowLeafIndex = indexOfPrevious.index; - this.cachedLeafPreimages[lowLeafIndex] = lowLeafPreimage; - await this.updateLeaf(lowLeafPreimage, BigInt(lowLeafIndex)); + this.cachedLeafPreimages[lowLeafIndex] = newLowLeafPreimage; + await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex)); } const newSubtreeSiblingPath = await this.getSubtreeSiblingPath( @@ -507,7 +512,7 @@ export class StandardIndexedTree extends TreeBase * @param hash0Leaf - Indicates whether 0 value leaf should be hashed. See {@link encodeLeaf}. * @returns Empty promise */ - private async encodeAndAppendLeaves(preimages: IndexedTreeLeafPreimage[], hash0Leaf: boolean): Promise { + private async encodeAndAppendLeaves(preimages: Preimage[], hash0Leaf: boolean): Promise { const startInsertionIndex = Number(this.getNumLeaves(true)); const hashedLeaves = preimages.map((preimage, i) => { @@ -526,9 +531,9 @@ export class StandardIndexedTree extends TreeBase * nullifier it is improbable that a valid nullifier would be 0. * @returns Leaf encoded in a buffer. */ - private encodeLeaf(leaf: IndexedTreeLeafPreimage, hash0Leaf: boolean): Buffer { + private encodeLeaf(leaf: Preimage, hash0Leaf: boolean): Buffer { let encodedLeaf; - if (!hash0Leaf && leaf.key == 0n) { + if (!hash0Leaf && leaf.getKey() == 0n) { encodedLeaf = toBufferBE(0n, 32); } else { encodedLeaf = this.hasher.hashInputs(leaf.toHashInputs()); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index b9154eb7dc6f..015193954ebe 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -1,5 +1,6 @@ +import { Fr, NullifierLeaf, NullifierLeafPreimage } from '@aztec/circuits.js'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { Hasher, NullifierLeaf, NullifierLeafPreimage, SiblingPath } from '@aztec/types'; +import { Hasher, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; @@ -41,8 +42,8 @@ const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: st ); }; -const createIndexedTreeLeaf = (value: number, nextIndex: number, nextValue: number) => { - return new NullifierLeafPreimage(BigInt(value), BigInt(nextValue), BigInt(nextIndex)).toHashInputs(); +const createIndexedTreeLeafHashInputs = (value: number, nextIndex: number, nextValue: number) => { + return new NullifierLeafPreimage(new Fr(value), new Fr(nextValue), BigInt(nextIndex)).toHashInputs(); }; const verifyCommittedState = async ( @@ -82,7 +83,7 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 0 0 0 0 0 0 0 0. */ - const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeaf(0, 0, 0)); + const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 0, 0)); const level1ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); const level2ZeroHash = pedersen.hash(level1ZeroHash, level1ZeroHash); @@ -116,8 +117,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 1 0 0 0 0 0 0 0 * nextVal 30 0 0 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeaf(0, 1, 30)); - let index1Hash = pedersen.hashInputs(createIndexedTreeLeaf(30, 0, 0)); + index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 1, 30)); + let index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, level1ZeroHash); root = pedersen.hash(e20, level2ZeroHash); @@ -143,8 +144,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 0 1 0 0 0 0 0 * nextVal 10 0 30 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeaf(0, 2, 10)); - let index2Hash = pedersen.hashInputs(createIndexedTreeLeaf(10, 1, 30)); + index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 2, 10)); + let index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 1, 30)); e10 = pedersen.hash(index0Hash, index1Hash); let e11 = pedersen.hash(index2Hash, INITIAL_LEAF); e20 = pedersen.hash(e10, e11); @@ -176,8 +177,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 10 0 20 30 0 0 0 0. */ e10 = pedersen.hash(index0Hash, index1Hash); - index2Hash = pedersen.hashInputs(createIndexedTreeLeaf(10, 3, 20)); - const index3Hash = pedersen.hashInputs(createIndexedTreeLeaf(20, 1, 30)); + index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 3, 20)); + const index3Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(20, 1, 30)); e11 = pedersen.hash(index2Hash, index3Hash); e20 = pedersen.hash(e10, e11); root = pedersen.hash(e20, level2ZeroHash); @@ -207,8 +208,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 4 3 1 0 0 0 0 * nextVal 10 50 20 30 0 0 0 0. */ - index1Hash = pedersen.hashInputs(createIndexedTreeLeaf(30, 4, 50)); - const index4Hash = pedersen.hashInputs(createIndexedTreeLeaf(50, 0, 0)); + index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 4, 50)); + const index4Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(50, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, e11); const e12 = pedersen.hash(index4Hash, INITIAL_LEAF); @@ -280,7 +281,7 @@ describe('StandardIndexedTreeSpecific', () => { */ const INITIAL_LEAF = toBufferBE(0n, 32); - const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeaf(0, 0, 0)); + const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 0, 0)); const level1ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); const level2ZeroHash = pedersen.hash(level1ZeroHash, level1ZeroHash); let index0Hash = initialLeafHash; @@ -314,8 +315,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 1 0 0 0 0 0 0 0 * nextVal 30 0 0 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeaf(0, 1, 30)); - let index1Hash = pedersen.hashInputs(createIndexedTreeLeaf(30, 0, 0)); + index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 1, 30)); + let index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, level1ZeroHash); root = pedersen.hash(e20, level2ZeroHash); @@ -340,8 +341,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 0 1 0 0 0 0 0 * nextVal 10 0 30 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeaf(0, 2, 10)); - let index2Hash = pedersen.hashInputs(createIndexedTreeLeaf(10, 1, 30)); + index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 2, 10)); + let index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 1, 30)); e10 = pedersen.hash(index0Hash, index1Hash); let e11 = pedersen.hash(index2Hash, INITIAL_LEAF); e20 = pedersen.hash(e10, e11); @@ -373,8 +374,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 10 0 20 30 0 0 0 0. */ e10 = pedersen.hash(index0Hash, index1Hash); - index2Hash = pedersen.hashInputs(createIndexedTreeLeaf(10, 3, 20)); - const index3Hash = pedersen.hashInputs(createIndexedTreeLeaf(20, 1, 30)); + index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 3, 20)); + const index3Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(20, 1, 30)); e11 = pedersen.hash(index2Hash, index3Hash); e20 = pedersen.hash(e10, e11); root = pedersen.hash(e20, level2ZeroHash); @@ -412,8 +413,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 6 3 1 0 0 0 0 * nextVal 10 50 20 30 0 0 0 0. */ - index1Hash = pedersen.hashInputs(createIndexedTreeLeaf(30, 6, 50)); - const index6Hash = pedersen.hashInputs(createIndexedTreeLeaf(50, 0, 0)); + index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 6, 50)); + const index6Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(50, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, e11); const e13 = pedersen.hash(index6Hash, INITIAL_LEAF); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 992cc31f112a..bd0e5d549b39 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -1,4 +1,4 @@ -import { IndexedTreeLeaf } from '@aztec/types'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { StandardIndexedTree } from '../../index.js'; @@ -7,7 +7,10 @@ import { StandardIndexedTree } from '../../index.js'; * that was replaced by the more efficient batchInsert method. We keep the original implementation around as it useful * for testing that the more complex batchInsert method works correctly. */ -export class StandardIndexedTreeWithAppend extends StandardIndexedTree { +export class StandardIndexedTreeWithAppend< + Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, +> extends StandardIndexedTree { /** * Appends the given leaves to the tree. * @param leaves - The leaves to append. @@ -39,15 +42,15 @@ export class StandardIndexedTreeWithAppend extends } const indexOfPrevious = this.findIndexOfPreviousKey(newLeaf.getKey(), true); - const previousLeafCopy = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true); + const lowLeafPreimage = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true); - if (previousLeafCopy === undefined) { + if (lowLeafPreimage === undefined) { throw new Error(`Previous leaf not found!`); } const newLeafPreimage = this.leafPreimageFactory.fromLeaf( newLeaf, - previousLeafCopy.nextKey, - previousLeafCopy.nextIndex, + lowLeafPreimage.getNextKey(), + lowLeafPreimage.getNextIndex(), ); if (indexOfPrevious.alreadyPresent) { @@ -55,11 +58,14 @@ export class StandardIndexedTreeWithAppend extends } // insert a new leaf at the highest index and update the values of our previous leaf copy const currentSize = this.getNumLeaves(true); - previousLeafCopy.nextIndex = BigInt(currentSize); - previousLeafCopy.nextKey = newLeaf.getKey(); + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + lowLeafPreimage.asLeaf(), + newLeaf.getKey(), + BigInt(currentSize), + ); this.cachedLeafPreimages[Number(currentSize)] = newLeafPreimage; - this.cachedLeafPreimages[Number(indexOfPrevious.index)] = previousLeafCopy; - await this.updateLeaf(previousLeafCopy, BigInt(indexOfPrevious.index)); + this.cachedLeafPreimages[Number(indexOfPrevious.index)] = newLowLeafPreimage; + await this.updateLeaf(newLowLeafPreimage, BigInt(indexOfPrevious.index)); await this.updateLeaf(newLeafPreimage, this.getNumLeaves(true)); } } diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index 8a539742be1f..3dbcdaecb9d3 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -1360,8 +1360,8 @@ export function mapNullifierLeafPreimageToNoir( nullifierLeafPreimage: NullifierLeafPreimage, ): NullifierLeafPreimageNoir { return { - leaf_value: mapFieldToNoir(nullifierLeafPreimage.leafValue), - next_value: mapFieldToNoir(nullifierLeafPreimage.nextValue), + leaf_value: mapFieldToNoir(nullifierLeafPreimage.nullifier), + next_value: mapFieldToNoir(nullifierLeafPreimage.nextNullifier), next_index: mapFieldToNoir(new Fr(nullifierLeafPreimage.nextIndex)), }; } diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index 464e766a69eb..e22fe389a7cb 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -22,6 +22,7 @@ import { NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + NullifierLeaf, NullifierLeafPreimage, PUBLIC_DATA_TREE_HEIGHT, PreviousKernelData, @@ -61,8 +62,6 @@ import { BlockBuilder } from './index.js'; import { AllowedTreeNames, OutputWithTreeSnapshot } from './types.js'; const frToBigInt = (fr: Fr) => toBigIntBE(fr.toBuffer()); -const bigintToFr = (num: bigint) => new Fr(num); -const bigintToNum = (num: bigint) => Number(num); // Denotes fields that are not used now, but will be in the future const FUTURE_FR = new Fr(0n); @@ -577,19 +576,15 @@ export class SoloBlockBuilder implements BlockBuilder { const tree = MerkleTreeId.NULLIFIER_TREE; const prevValueIndex = await this.db.getPreviousValueIndex(tree, frToBigInt(nullifier)); - const prevValueInfo = await this.db.getLeafData(tree, prevValueIndex.index); - if (!prevValueInfo) { + const prevValuePreimage = await this.db.getLeafPreimage(tree, prevValueIndex.index); + if (!prevValuePreimage) { throw new Error(`Nullifier tree should have one initial leaf`); } const prevValueSiblingPath = await this.db.getSiblingPath(tree, BigInt(prevValueIndex.index)); return { index: prevValueIndex, - leafPreimage: new NullifierLeafPreimage( - bigintToFr(prevValueInfo.value), - bigintToFr(prevValueInfo.nextValue), - bigintToNum(prevValueInfo.nextIndex), - ), + leafPreimage: prevValuePreimage, witness: new MembershipWitness( NULLIFIER_TREE_HEIGHT, BigInt(prevValueIndex.index), @@ -713,7 +708,12 @@ export class SoloBlockBuilder implements BlockBuilder { newSubtreeSiblingPath: newNullifiersSubtreeSiblingPath, sortedNewLeaves: sortedNewNullifiers, sortedNewLeavesIndexes: sortednewNullifiersIndexes, - } = await this.db.batchInsert( + } = await this.db.batchInsert< + typeof NULLIFIER_TREE_HEIGHT, + typeof NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, + NullifierLeaf, + NullifierLeafPreimage + >( MerkleTreeId.NULLIFIER_TREE, newNullifiers.map(fr => fr.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, @@ -747,13 +747,7 @@ export class SoloBlockBuilder implements BlockBuilder { newPublicDataUpdateRequestsSiblingPaths, newPublicDataReadsSiblingPaths, lowNullifierLeafPreimages: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => - i < nullifierWitnessLeaves.length - ? new NullifierLeafPreimage( - new Fr(nullifierWitnessLeaves[i].leafData.value), - new Fr(nullifierWitnessLeaves[i].leafData.nextValue), - Number(nullifierWitnessLeaves[i].leafData.nextIndex), - ) - : new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0), + i < nullifierWitnessLeaves.length ? nullifierWitnessLeaves[i].leafData : NullifierLeafPreimage.empty(), ), lowNullifierMembershipWitness: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i < lowNullifierMembershipWitnesses.length diff --git a/yarn-project/types/src/interfaces/indexed_tree.ts b/yarn-project/types/src/interfaces/indexed_tree.ts index d2a63e0dbd27..2edc8e09818e 100644 --- a/yarn-project/types/src/interfaces/indexed_tree.ts +++ b/yarn-project/types/src/interfaces/indexed_tree.ts @@ -15,22 +15,3 @@ export interface LeafData { */ nextValue: bigint; } - -/* eslint-disable */ - -export interface IndexedTreeLeaf { - getKey(): bigint; - toBuffer(): Buffer; - isEmpty(): boolean; -} - -export interface IndexedTreeLeafPreimage { - key: bigint; - nextKey: bigint; - nextIndex: bigint; - - asLeaf(): Leaf; - toBuffer(): Buffer; - toHashInputs(): Buffer[]; - clone(): IndexedTreeLeafPreimage; -} diff --git a/yarn-project/types/src/interfaces/nullifier_tree.ts b/yarn-project/types/src/interfaces/nullifier_tree.ts index 180472a988e0..ab3053ef858c 100644 --- a/yarn-project/types/src/interfaces/nullifier_tree.ts +++ b/yarn-project/types/src/interfaces/nullifier_tree.ts @@ -1,8 +1,6 @@ -import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; -import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { Fr, NULLIFIER_TREE_HEIGHT, NullifierLeafPreimage } from '@aztec/circuits.js'; import { SiblingPath } from '../sibling_path.js'; -import { IndexedTreeLeaf, IndexedTreeLeafPreimage, LeafData } from './indexed_tree.js'; /** * Nullifier membership witness. @@ -19,7 +17,7 @@ export class NullifierMembershipWitness { /** * Preimage of the nullifier. */ - public readonly leafData: LeafData, + public readonly leafData: NullifierLeafPreimage, /** * Sibling path to prove membership of the nullifier. */ @@ -33,75 +31,10 @@ export class NullifierMembershipWitness { public toFieldArray(): Fr[] { return [ new Fr(this.index), - new Fr(this.leafData.value), + new Fr(this.leafData.nullifier), new Fr(this.leafData.nextIndex), - new Fr(this.leafData.nextValue), + new Fr(this.leafData.nextNullifier), ...this.siblingPath.toFieldArray(), ]; } } - -/* eslint-disable */ - -export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { - constructor(public key: bigint, public nextKey: bigint, public nextIndex: bigint) {} - - asLeaf(): NullifierLeaf { - return new NullifierLeaf(new Fr(this.key)); - } - - toBuffer(): Buffer { - return Buffer.concat(this.toHashInputs()); - } - - toHashInputs(): Buffer[] { - return [ - Buffer.from(toBufferBE(this.key, 32)), - Buffer.from(toBufferBE(this.nextIndex, 32)), - Buffer.from(toBufferBE(this.nextKey, 32)), - ]; - } - - clone(): NullifierLeafPreimage { - return new NullifierLeafPreimage(this.key, this.nextKey, this.nextIndex); - } - - static empty(): NullifierLeafPreimage { - return new NullifierLeafPreimage(0n, 0n, 0n); - } - - static fromBuffer(buf: Buffer): NullifierLeafPreimage { - const key = toBigIntBE(buf.subarray(0, 32)); - const nextKey = toBigIntBE(buf.subarray(32, 64)); - const nextIndex = toBigIntBE(buf.subarray(64, 96)); - return new NullifierLeafPreimage(key, nextKey, nextIndex); - } - - static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { - return new NullifierLeafPreimage(leaf.nullifier.toBigInt(), nextKey, nextIndex); - } -} - -export class NullifierLeaf implements IndexedTreeLeaf { - constructor(public nullifier: Fr) {} - - getKey(): bigint { - return this.nullifier.toBigInt(); - } - - toBuffer(): Buffer { - return this.nullifier.toBuffer(); - } - - isEmpty(): boolean { - return this.nullifier.isZero(); - } - - static buildDummy(key: bigint): NullifierLeaf { - return new NullifierLeaf(new Fr(key)); - } - - static fromBuffer(buf: Buffer): NullifierLeaf { - return new NullifierLeaf(Fr.fromBuffer(buf)); - } -} diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 5725ec141fe2..35594003f6a8 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,6 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeDb, MerkleTreeOperations, TreeInfo } from '../index.js'; @@ -89,12 +90,12 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * @param index - The index of the leaf to get. * @returns Leaf data. */ - async getLeafPreimage( + async getLeafPreimage>( treeId: MerkleTreeId.NULLIFIER_TREE, index: number, - ): Promise | undefined> { + ): Promise { const preimage = await this.trees.getLeafPreimage(treeId, index, this.includeUncommitted); - return preimage as IndexedTreeLeafPreimage | undefined; + return preimage as Preimage | undefined; } /** @@ -175,11 +176,16 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * @param subtreeHeight - Height of the subtree. * @returns The data for the leaves to be updated when inserting the new ones. */ - public batchInsert( + public batchInsert< + TreeHeight extends number, + SubtreeSiblingPathHeight extends number, + Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, + >( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: number, - ): Promise> { + ): Promise> { return this.trees.batchInsert(treeId, leaves, subtreeHeight); } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 40be71456aae..25b80d9c3ecb 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -1,8 +1,9 @@ import { MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; /** * Type alias for the nullifier tree ID. @@ -146,10 +147,10 @@ export interface MerkleTreeOperations { * @param treeId - The tree for which leaf data should be returned. * @param index - The index of the leaf required. */ - getLeafPreimage( + getLeafPreimage>( treeId: IndexedTreeId, index: number, - ): Promise | undefined>; + ): Promise; /** * Update the leaf data at the given index. @@ -198,11 +199,16 @@ export interface MerkleTreeOperations { * @param subtreeHeight - Height of the subtree. * @returns The witness data for the leaves to be updated when inserting the new ones. */ - batchInsert( + batchInsert< + TreeHeight extends number, + SubtreeSiblingPathHeight extends number, + Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, + >( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: number, - ): Promise>; + ): Promise>; /** * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 577705dac81f..341404f3ae63 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -7,12 +7,15 @@ import { NOTE_HASH_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + NullifierLeaf, + NullifierLeafPreimage, PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computeBlockHash, computeGlobalsHash } from '@aztec/circuits.js/abis'; import { Committable } from '@aztec/foundation/committable'; import { SerialQueue } from '@aztec/foundation/fifo'; import { createDebugLogger } from '@aztec/foundation/log'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { AppendOnlyTree, BatchInsertionResult, @@ -24,8 +27,9 @@ import { UpdateOnlyTree, loadTree, newTree, + treeBuilder, } from '@aztec/merkle-tree'; -import { IndexedTreeLeaf, IndexedTreeLeafPreimage, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { Hasher, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; @@ -75,14 +79,16 @@ export class MerkleTrees implements MerkleTreeDb { const hasher = new Pedersen(); const contractTree: AppendOnlyTree = await initializeTree( - StandardTree, + treeBuilder(StandardTree), this.db, hasher, `${MerkleTreeId[MerkleTreeId.CONTRACT_TREE]}`, CONTRACT_TREE_HEIGHT, ); const nullifierTree = await initializeTree( - StandardIndexedTree, + (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { + return new StandardIndexedTree(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf); + }, this.db, hasher, `${MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]}`, @@ -90,28 +96,28 @@ export class MerkleTrees implements MerkleTreeDb { INITIAL_NULLIFIER_TREE_SIZE, ); const noteHashTree: AppendOnlyTree = await initializeTree( - StandardTree, + treeBuilder(StandardTree), this.db, hasher, `${MerkleTreeId[MerkleTreeId.NOTE_HASH_TREE]}`, NOTE_HASH_TREE_HEIGHT, ); const publicDataTree: UpdateOnlyTree = await initializeTree( - SparseTree, + treeBuilder(SparseTree), this.db, hasher, `${MerkleTreeId[MerkleTreeId.PUBLIC_DATA_TREE]}`, PUBLIC_DATA_TREE_HEIGHT, ); const l1Tol2MessagesTree: AppendOnlyTree = await initializeTree( - StandardTree, + treeBuilder(StandardTree), this.db, hasher, `${MerkleTreeId[MerkleTreeId.L1_TO_L2_MESSAGES_TREE]}`, L1_TO_L2_MSG_TREE_HEIGHT, ); const historicBlocksTree: AppendOnlyTree = await initializeTree( - StandardTree, + treeBuilder(StandardTree), this.db, hasher, `${MerkleTreeId[MerkleTreeId.BLOCKS_TREE]}`, @@ -332,13 +338,15 @@ export class MerkleTrees implements MerkleTreeDb { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns Leaf data. */ - public async getLeafPreimage( + public async getLeafPreimage>( treeId: IndexedTreeId, index: number, includeUncommitted: boolean, ): Promise | undefined> { return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted)), + Promise.resolve( + this._getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted), + ), ); } @@ -398,12 +406,13 @@ export class MerkleTrees implements MerkleTreeDb { SubtreeHeight extends number, SubtreeSiblingPathHeight extends number, Leaf extends IndexedTreeLeaf, + Preimage extends IndexedTreeLeafPreimage, >( treeId: MerkleTreeId, leaves: Buffer[], subtreeHeight: SubtreeHeight, - ): Promise> { - const tree = this.trees[treeId] as StandardIndexedTree; + ): Promise> { + const tree = this.trees[treeId] as StandardIndexedTree; if (!('batchInsert' in tree)) { throw new Error('Tree does not support `batchInsert` method'); } @@ -454,8 +463,10 @@ export class MerkleTrees implements MerkleTreeDb { * @param treeId - Id of the tree to get an instance of. * @returns The indexed tree for the specified tree id. */ - private _getIndexedTree(treeId: IndexedTreeId): IndexedTree { - return this.trees[treeId] as IndexedTree; + private _getIndexedTree>( + treeId: IndexedTreeId, + ): IndexedTree { + return this.trees[treeId] as IndexedTree; } /** @@ -560,7 +571,9 @@ export class MerkleTrees implements MerkleTreeDb { } // Sync the indexed trees - await (this.trees[MerkleTreeId.NULLIFIER_TREE] as StandardIndexedTree).batchInsert( + await ( + this.trees[MerkleTreeId.NULLIFIER_TREE] as StandardIndexedTree + ).batchInsert( l2Block.newNullifiers.map(fr => fr.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, ); From 38fcf05080852985242e4a7456847a818bcf3f20 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 13:05:18 +0000 Subject: [PATCH 03/40] chore: add test dep to devDependencies --- yarn-project/merkle-tree/package.json | 1 + yarn-project/yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index 6418d219963b..4c7b53d8f423 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -40,6 +40,7 @@ "tslib": "^2.4.0" }, "devDependencies": { + "@aztec/circuits.js": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.2", diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 4c4ad8d3a130..ebd3927a90fa 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -549,6 +549,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/merkle-tree@workspace:merkle-tree" dependencies: + "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 From bc4915b1cf05b8b08b6f395997e242a13ca793cd Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 13:11:42 +0000 Subject: [PATCH 04/40] chore: remove old LeafData struct --- yarn-project/end-to-end/tsconfig.json | 4 +--- .../src/interfaces/update_only_tree.ts | 5 +---- yarn-project/merkle-tree/tsconfig.json | 3 +++ yarn-project/types/src/interfaces/index.ts | 1 - .../types/src/interfaces/indexed_tree.ts | 17 ----------------- .../merkle_tree_operations_facade.ts | 5 +++-- .../src/world-state-db/merkle_tree_db.ts | 6 +++--- .../src/world-state-db/merkle_trees.ts | 10 +++------- 8 files changed, 14 insertions(+), 37 deletions(-) delete mode 100644 yarn-project/types/src/interfaces/indexed_tree.ts diff --git a/yarn-project/end-to-end/tsconfig.json b/yarn-project/end-to-end/tsconfig.json index 8792db4c3c58..c44790b98d8d 100644 --- a/yarn-project/end-to-end/tsconfig.json +++ b/yarn-project/end-to-end/tsconfig.json @@ -55,7 +55,5 @@ "path": "../world-state" } ], - "include": [ - "src" - ] + "include": ["src"] } diff --git a/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts b/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts index 59a82d0b1184..d798c5bf4354 100644 --- a/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts @@ -1,5 +1,3 @@ -import { LeafData } from '@aztec/types'; - import { MerkleTree } from './merkle_tree.js'; /** @@ -11,6 +9,5 @@ export interface UpdateOnlyTree extends MerkleTree { * @param leaf - The leaf value to be updated. * @param index - The leaf to be updated. */ - // TODO: Make this strictly a Buffer - updateLeaf(leaf: Buffer | LeafData, index: bigint): Promise; + updateLeaf(leaf: Buffer, index: bigint): Promise; } diff --git a/yarn-project/merkle-tree/tsconfig.json b/yarn-project/merkle-tree/tsconfig.json index 831130c7c84b..35f81f8b801f 100644 --- a/yarn-project/merkle-tree/tsconfig.json +++ b/yarn-project/merkle-tree/tsconfig.json @@ -11,6 +11,9 @@ }, { "path": "../types" + }, + { + "path": "../circuits.js" } ], "include": ["src"] diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index 9832cc337ffa..cdba82c68472 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -6,5 +6,4 @@ export * from './deployed-contract.js'; export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; -export * from './indexed_tree.js'; export * from './nullifier_tree.js'; diff --git a/yarn-project/types/src/interfaces/indexed_tree.ts b/yarn-project/types/src/interfaces/indexed_tree.ts deleted file mode 100644 index 2edc8e09818e..000000000000 --- a/yarn-project/types/src/interfaces/indexed_tree.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * A leaf of a tree. - */ -export interface LeafData { - /** - * A value of the leaf. - */ - value: bigint; - /** - * An index of the next leaf. - */ - nextIndex: bigint; - /** - * A value of the next leaf. - */ - nextValue: bigint; -} diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 35594003f6a8..d5ddc2025bfc 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,7 +1,8 @@ +import { NullifierLeafPreimage } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeDb, MerkleTreeOperations, TreeInfo } from '../index.js'; @@ -80,7 +81,7 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * @param index - The index to insert into. * @returns Empty promise. */ - updateLeaf(treeId: MerkleTreeId.NULLIFIER_TREE, leaf: LeafData, index: bigint): Promise { + updateLeaf(treeId: MerkleTreeId.NULLIFIER_TREE, leaf: NullifierLeafPreimage, index: bigint): Promise { return this.trees.updateLeaf(treeId, leaf, index); } diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 25b80d9c3ecb..7c3aeba073e1 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -1,9 +1,9 @@ -import { MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js'; +import { MAX_NEW_NULLIFIERS_PER_TX, NullifierLeafPreimage } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; -import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; /** * Type alias for the nullifier tree ID. @@ -158,7 +158,7 @@ export interface MerkleTreeOperations { * @param leaf - The updated leaf value. * @param index - The index of the leaf to be updated. */ - updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: LeafData | Buffer, index: bigint): Promise; + updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: NullifierLeafPreimage | Buffer, index: bigint): Promise; /** * Returns the index containing a leaf value. diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 341404f3ae63..b5361513d034 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -29,7 +29,7 @@ import { newTree, treeBuilder, } from '@aztec/merkle-tree'; -import { Hasher, L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { Hasher, L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; @@ -381,7 +381,7 @@ export class MerkleTrees implements MerkleTreeDb { * @param index - The index to insert into. * @returns Empty promise. */ - public async updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: LeafData | Buffer, index: bigint): Promise { + public async updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: Buffer, index: bigint): Promise { return await this.synchronize(() => this._updateLeaf(treeId, leaf, index)); } @@ -498,11 +498,7 @@ export class MerkleTrees implements MerkleTreeDb { return await tree.appendLeaves(leaves); } - private async _updateLeaf( - treeId: IndexedTreeId | PublicTreeId, - leaf: LeafData | Buffer, - index: bigint, - ): Promise { + private async _updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: Buffer, index: bigint): Promise { const tree = this.trees[treeId]; if (!('updateLeaf' in tree)) { throw new Error('Tree does not support `updateLeaf` method'); From 47a3aee14df7b7a06e402e49735c2ca34d0df460 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 13:34:35 +0000 Subject: [PATCH 05/40] fix: getLatestLeafPreimage can return undefined --- .../src/standard_indexed_tree/standard_indexed_tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 32c8944bcadb..5753a258a47a 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -157,7 +157,7 @@ export class StandardIndexedTree Date: Fri, 1 Dec 2023 15:39:29 +0000 Subject: [PATCH 06/40] feat: remove local leaf cache in indexed tree --- .../aztec-node/src/aztec-node/server.ts | 2 +- .../src/structs/rollup/base_rollup.ts | 8 +- .../src/interfaces/indexed_tree.ts | 11 +- yarn-project/merkle-tree/src/load_tree.ts | 1 - .../standard_indexed_tree.ts | 192 +++++++++--------- .../test/standard_indexed_tree_with_append.ts | 10 +- yarn-project/merkle-tree/src/tree_base.ts | 7 - .../noir-protocol-circuits/src/index.ts | 1 + .../src/type_conversion.ts | 5 +- .../merkle_tree_operations_facade.ts | 4 +- .../src/world-state-db/merkle_tree_db.ts | 4 +- .../src/world-state-db/merkle_trees.ts | 8 +- 12 files changed, 124 insertions(+), 129 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index a197bbfccd17..53aa86cc19b9 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -405,7 +405,7 @@ export class AztecNodeService implements AztecNode { const leafDataPromise = committedDb.getLeafPreimage( MerkleTreeId.NULLIFIER_TREE, - Number(index), + index, ); const siblingPathPromise = committedDb.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts index 894f3ec0d933..bbc2b03b4e43 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts @@ -81,10 +81,10 @@ export class NullifierLeafPreimage implements IndexedTreeLeafPreimage; /** * Gets the latest LeafPreimage copy. @@ -85,7 +85,10 @@ export interface IndexedTree | undefined; + getLatestLeafPreimageCopy( + index: bigint, + includeUncommitted: boolean, + ): Promise | undefined>; /** * Batch insert multiple leaves into the tree. diff --git a/yarn-project/merkle-tree/src/load_tree.ts b/yarn-project/merkle-tree/src/load_tree.ts index 7bbb85634aa1..bd852b930fd9 100644 --- a/yarn-project/merkle-tree/src/load_tree.ts +++ b/yarn-project/merkle-tree/src/load_tree.ts @@ -22,6 +22,5 @@ export async function loadTree( const { root, depth, size } = decodeMeta(meta); const tree = c(db, hasher, name, depth, size, root); - await tree.initFromDb(); return tree; } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 5753a258a47a..a1dcce184097 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -34,21 +34,19 @@ interface PreimageFactory { - return `${name}:leaf:${toBufferBE(key, 32).toString('hex')}`; +const leafIndexToDbKey = (name: string, index: bigint) => { + return `${name}:leaf_by_index:${toBufferBE(index, 32).toString('hex')}`; }; -const dbKeyToLeafKey = (key: string): bigint => { - const index = key.split(':')[2]; - return toBigIntBE(Buffer.from(index, 'hex')); +const leafKeyToDbKey = (name: string, key: bigint) => { + return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}`; }; export class StandardIndexedTree> extends TreeBase implements IndexedTree { - protected leafPreimages: Preimage[] = []; - protected cachedLeafPreimages: { [key: number]: Preimage } = {}; + protected cachedLeafPreimages: { [key: string]: Preimage } = {}; public constructor( db: LevelUp, @@ -98,12 +96,9 @@ export class StandardIndexedTree { - const leaf = this.getLatestLeafPreimageCopy(Number(index), includeUncommitted); - if (!leaf) { - return Promise.resolve(undefined); - } - return Promise.resolve(leaf.toBuffer()); + public async getLeafValue(index: bigint, includeUncommitted: boolean): Promise { + const leaf = await this.getLatestLeafPreimageCopy(index, includeUncommitted); + return leaf && leaf.toBuffer(); } /** @@ -112,39 +107,87 @@ export class StandardIndexedTree newValue) { - diff.push(newValue); - } else if (storedPreimage.getKey() === newValue) { - return { index: i, alreadyPresent: true }; - } else { - diff.push(newValue - storedPreimage.getKey()); + }> { + let lowLeafIndex = await this.getDbLowLeafIndex(newKey); + let lowLeafPreimage = lowLeafIndex !== undefined ? await this.getDbPreimage(lowLeafIndex) : undefined; + + if (includeUncommitted) { + const cachedLowLeafIndex = this.getCachedLowLeafIndex(newKey); + const cachedLowLeafPreimage = + cachedLowLeafIndex !== undefined ? this.getCachedPreimage(cachedLowLeafIndex) : undefined; + if (cachedLowLeafIndex && (!lowLeafIndex || cachedLowLeafPreimage!.getKey() > lowLeafPreimage!.getKey())) { + lowLeafIndex = cachedLowLeafIndex; + lowLeafPreimage = cachedLowLeafPreimage; } } - const minIndex = this.findMinKey(diff); - return { index: minIndex, alreadyPresent: false }; + + if (lowLeafIndex === undefined || !lowLeafPreimage) { + throw new Error('Low leaf not found'); + } + + return { + index: lowLeafIndex, + alreadyPresent: lowLeafPreimage.getKey() === newKey, + }; + } + + private getCachedLowLeafIndex(key: bigint): bigint | undefined { + const indexes = Object.getOwnPropertyNames(this.cachedLeafPreimages); + const lowLeafIndexes = indexes + .map(index => ({ + index: BigInt(index), + key: this.cachedLeafPreimages[index].getKey(), + })) + .filter(({ key: candidateKey }) => candidateKey <= key) + .sort((a, b) => Number(b.key - a.key)); + return lowLeafIndexes[0]?.index; + } + + private async getDbLowLeafIndex(key: bigint): Promise { + return await new Promise((resolve, reject) => { + let lowLeafIndex: bigint | undefined; + this.db + .createReadStream({ + lte: leafKeyToDbKey(this.getName(), key), + limit: 1, + reverse: true, + }) + .on('data', data => { + lowLeafIndex = toBigIntBE(data.value); + }) + .on('close', function () {}) + .on('end', function () { + resolve(lowLeafIndex); + }) + .on('error', function () { + log.error('stream error'); + reject(); + }); + }); + } + + private async getDbPreimage(index: bigint): Promise { + const dbPreimage = await this.db + .get(leafIndexToDbKey(this.getName(), index)) + .then(data => this.leafPreimageFactory.fromBuffer(data)) + .catch(() => undefined); + return dbPreimage; + } + + private getCachedPreimage(index: bigint): Preimage | undefined { + return this.cachedLeafPreimages[index.toString()]; } /** @@ -153,31 +196,13 @@ export class StandardIndexedTree { const preimage = !includeUncommitted - ? this.leafPreimages[index] - : this.cachedLeafPreimages[index] ?? this.leafPreimages[index]; + ? await this.getDbPreimage(index) + : this.getCachedPreimage(index) ?? (await this.getDbPreimage(index)); return preimage && this.leafPreimageFactory.clone(preimage); } - /** - * Finds the index of the minimum value in an array. - * @param values - The collection of values to be searched. - * @returns The index of the minimum value in the array. - */ - private findMinKey(values: bigint[]) { - if (!values.length) { - return 0; - } - let minIndex = 0; - for (let i = 1; i < values.length; i++) { - if (values[minIndex] > values[i]) { - minIndex = i; - } - } - return minIndex; - } - /** * Initializes the tree. * @param prefilledSize - A number of leaves that are prefilled with values. @@ -211,35 +236,6 @@ export class StandardIndexedTree { - const startingIndex = 0n; - const preimages: Preimage[] = []; - const promise = new Promise((resolve, reject) => { - this.db - .createReadStream({ - gte: leafKeyToDbKey(this.getName(), startingIndex), - lte: leafKeyToDbKey(this.getName(), 2n ** BigInt(this.getDepth())), - }) - .on('data', data => { - const leafKey = dbKeyToLeafKey(data.key.toString('utf-8')); - preimages[Number(leafKey)] = this.leafPreimageFactory.fromBuffer(data.value); - }) - .on('close', function () {}) - .on('end', function () { - resolve(); - }) - .on('error', function () { - log.error('stream error'); - reject(); - }); - }); - await promise; - this.leafPreimages = preimages; - } - /** * Commits all the leaves to the database and removes them from a cache. */ @@ -247,9 +243,10 @@ export class StandardIndexedTree( + getEmptyLowLeafWitness( treeHeight: N, preimageFactory: Empty, ): LowLeafWitnessData { @@ -412,7 +410,7 @@ export class StandardIndexedTree> { - const emptyLowLeafWitness = this.getEmptyLowLeafWitnes(this.getDepth() as TreeHeight, this.leafPreimageFactory); + const emptyLowLeafWitness = this.getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); // Accumulators const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); const pendingInsertionSubtree: Preimage[] = leaves.map(() => this.leafPreimageFactory.empty()); @@ -435,10 +433,10 @@ export class StandardIndexedTree( @@ -513,10 +511,10 @@ export class StandardIndexedTree { - const startInsertionIndex = Number(this.getNumLeaves(true)); + const startInsertionIndex = this.getNumLeaves(true); const hashedLeaves = preimages.map((preimage, i) => { - this.cachedLeafPreimages[startInsertionIndex + i] = preimage; + this.cachedLeafPreimages[(startInsertionIndex + BigInt(i)).toString()] = preimage; return this.encodeLeaf(preimage, hash0Leaf); }); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index bd0e5d549b39..047aea52e199 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -41,8 +41,8 @@ export class StandardIndexedTreeWithAppend< return; } - const indexOfPrevious = this.findIndexOfPreviousKey(newLeaf.getKey(), true); - const lowLeafPreimage = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true); + const lowLeafIndex = await this.findIndexOfPreviousKey(newLeaf.getKey(), true); + const lowLeafPreimage = await this.getLatestLeafPreimageCopy(lowLeafIndex.index, true); if (lowLeafPreimage === undefined) { throw new Error(`Previous leaf not found!`); @@ -53,7 +53,7 @@ export class StandardIndexedTreeWithAppend< lowLeafPreimage.getNextIndex(), ); - if (indexOfPrevious.alreadyPresent) { + if (lowLeafIndex.alreadyPresent) { return; } // insert a new leaf at the highest index and update the values of our previous leaf copy @@ -64,8 +64,8 @@ export class StandardIndexedTreeWithAppend< BigInt(currentSize), ); this.cachedLeafPreimages[Number(currentSize)] = newLeafPreimage; - this.cachedLeafPreimages[Number(indexOfPrevious.index)] = newLowLeafPreimage; - await this.updateLeaf(newLowLeafPreimage, BigInt(indexOfPrevious.index)); + this.cachedLeafPreimages[Number(lowLeafIndex.index)] = newLowLeafPreimage; + await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); await this.updateLeaf(newLeafPreimage, this.getNumLeaves(true)); } } diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index 6b7152803809..f7e3fae9639f 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -221,13 +221,6 @@ export abstract class TreeBase implements MerkleTree { await this.writeMeta(); } - /** - * Initializes the tree from the database. - */ - public async initFromDb(): Promise { - // Implemented only by Indexed Tree to populate the leaf cache. - } - /** * Writes meta data to the provided batch. * @param batch - The batch to which to write the meta data. diff --git a/yarn-project/noir-protocol-circuits/src/index.ts b/yarn-project/noir-protocol-circuits/src/index.ts index 97671183a7c6..dc5baffd5fd3 100644 --- a/yarn-project/noir-protocol-circuits/src/index.ts +++ b/yarn-project/noir-protocol-circuits/src/index.ts @@ -416,6 +416,7 @@ async function executeMergeRollupWithACVM(input: MergeRollupInputType): Promise< * Executes the base rollup with the given inputs using the acvm. */ async function executeBaseRollupWithACVM(input: BaseRollupInputType): Promise { + // console.log(JSON.stringify(input, null, 4)); const initialWitnessMap = abiEncode(BaseRollupJson.abi as Abi, input as any); // Execute the circuit on those initial witness values diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index 3dbcdaecb9d3..afb3acee7314 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -160,6 +160,9 @@ export function mapNumberFromNoir(number: NoirField): number { * */ export function mapNumberToNoir(number: number): NoirField { + if (number > 2 ** 32) { + throw new Error('Number out of range'); + } return new Fr(BigInt(number)).toString(); } @@ -1362,7 +1365,7 @@ export function mapNullifierLeafPreimageToNoir( return { leaf_value: mapFieldToNoir(nullifierLeafPreimage.nullifier), next_value: mapFieldToNoir(nullifierLeafPreimage.nextNullifier), - next_index: mapFieldToNoir(new Fr(nullifierLeafPreimage.nextIndex)), + next_index: mapNumberToNoir(Number(nullifierLeafPreimage.nextIndex)), }; } diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index d5ddc2025bfc..52b14fb637b6 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -65,7 +65,7 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { /** * The index of the found leaf. */ - index: number; + index: bigint; /** * A flag indicating if the corresponding leaf's value is equal to `newValue`. */ @@ -93,7 +93,7 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { */ async getLeafPreimage>( treeId: MerkleTreeId.NULLIFIER_TREE, - index: number, + index: bigint, ): Promise { const preimage = await this.trees.getLeafPreimage(treeId, index, this.includeUncommitted); return preimage as Preimage | undefined; diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 7c3aeba073e1..f94808728e7b 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -135,7 +135,7 @@ export interface MerkleTreeOperations { /** * The index of the found leaf. */ - index: number; + index: bigint; /** * A flag indicating if the corresponding leaf's value is equal to `newValue`. */ @@ -149,7 +149,7 @@ export interface MerkleTreeOperations { */ getLeafPreimage>( treeId: IndexedTreeId, - index: number, + index: bigint, ): Promise; /** diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index b5361513d034..91d7640210a8 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -320,15 +320,13 @@ export class MerkleTrees implements MerkleTreeDb { /** * The index of the found leaf. */ - index: number; + index: bigint; /** * A flag indicating if the corresponding leaf's value is equal to `newValue`. */ alreadyPresent: boolean; }> { - return await this.synchronize(() => - Promise.resolve(this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)), - ); + return await this.synchronize(() => this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)); } /** @@ -340,7 +338,7 @@ export class MerkleTrees implements MerkleTreeDb { */ public async getLeafPreimage>( treeId: IndexedTreeId, - index: number, + index: bigint, includeUncommitted: boolean, ): Promise | undefined> { return await this.synchronize(() => From d458756c1f65b8c42429656e25b98799cddb01d7 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 1 Dec 2023 15:51:14 +0000 Subject: [PATCH 07/40] feat: optimize findLeafIndex for indexed tree --- .../merkle-tree/src/interfaces/merkle_tree.ts | 8 +++++ .../standard_indexed_tree.ts | 31 +++++++++++++++++++ yarn-project/merkle-tree/src/tree_base.ts | 17 ++++++++++ .../src/world-state-db/merkle_trees.ts | 8 +---- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts b/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts index e4f65b326a22..ba3ffb4309b2 100644 --- a/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts @@ -49,4 +49,12 @@ export interface MerkleTree extends SiblingPathSource { * @param includeUncommitted - Set to true to include uncommitted updates in the data set. */ getLeafValue(index: bigint, includeUncommitted: boolean): Promise; + + /** + * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. + * @param leaf - The leaf value to look for. + * @param includeUncommitted - Indicates whether to include uncommitted data. + * @returns The index of the first leaf found with a given value (undefined if not found). + */ + findLeafIndex(leaf: Buffer, includeUncommitted: boolean): Promise; } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index a1dcce184097..9e262aa63d77 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -155,6 +155,16 @@ export class StandardIndexedTree { + return this.cachedLeafPreimages[index].getKey() === key; + }); + if (index) { + return BigInt(index); + } + return undefined; + } + private async getDbLowLeafIndex(key: bigint): Promise { return await new Promise((resolve, reject) => { let lowLeafIndex: bigint | undefined; @@ -203,6 +213,27 @@ export class StandardIndexedTree { + let leaf = this.leafFactory.fromBuffer(value); + let index = await this.db + .get(leafKeyToDbKey(this.getName(), leaf.getKey())) + .then(data => toBigIntBE(data)) + .catch(() => undefined); + + if (includeUncommitted && index === undefined) { + const cachedIndex = this.getCachedLeafIndex(leaf.getKey()); + index = cachedIndex; + } + return index; + } + /** * Initializes the tree. * @param prefilledSize - A number of leaves that are prefilled with values. diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index f7e3fae9639f..ff2b8baf3f65 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -280,4 +280,21 @@ export abstract class TreeBase implements MerkleTree { } this.cachedSize = numLeaves + BigInt(leaves.length); } + + /** + * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. + * @param treeId - The ID of the tree. + * @param value - The leaf value to look for. + * @param includeUncommitted - Indicates whether to include uncommitted data. + * @returns The index of the first leaf found with a given value (undefined if not found). + */ + public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + for (let i = 0n; i < this.getNumLeaves(includeUncommitted); i++) { + const currentValue = await this.getLeafValue(i, includeUncommitted); + if (currentValue && currentValue.equals(value)) { + return i; + } + } + return undefined; + } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 91d7640210a8..1e6a98e43a72 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -362,13 +362,7 @@ export class MerkleTrees implements MerkleTreeDb { ): Promise { return await this.synchronize(async () => { const tree = this.trees[treeId]; - for (let i = 0n; i < tree.getNumLeaves(includeUncommitted); i++) { - const currentValue = await tree.getLeafValue(i, includeUncommitted); - if (currentValue && currentValue.equals(value)) { - return i; - } - } - return undefined; + return await tree.findLeafIndex(value, includeUncommitted); }); } From ae6b6a0bb2c47457b80f58c5bfe31967210ad725 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 09:26:43 +0000 Subject: [PATCH 08/40] docs: added jsdoc --- yarn-project/foundation/src/trees/index.ts | 35 +++++++- .../src/interfaces/indexed_tree.ts | 2 - .../standard_indexed_tree.ts | 88 +++++++++++-------- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/yarn-project/foundation/src/trees/index.ts b/yarn-project/foundation/src/trees/index.ts index 139b63836205..3c60bec3efed 100644 --- a/yarn-project/foundation/src/trees/index.ts +++ b/yarn-project/foundation/src/trees/index.ts @@ -1,17 +1,48 @@ -/* eslint-disable */ - +/** + * A leaf of an indexed merkle tree. + */ export interface IndexedTreeLeaf { + /** + * Returns key of the leaf. It's used for indexing. + */ getKey(): bigint; + /** + * Serializes the leaf into a buffer. + */ toBuffer(): Buffer; + /** + * Returns true if the leaf is empty. + */ isEmpty(): boolean; } +/** + * Preimage of an indexed merkle tree leaf. + */ export interface IndexedTreeLeafPreimage { + /** + * Returns key of the leaf corresponding to this preimage. + */ getKey(): bigint; + /** + * Returns the key of the next leaf. + */ getNextKey(): bigint; + /** + * Returns the index of the next leaf. + */ getNextIndex(): bigint; + /** + * Returns the preimage as a leaf. + */ asLeaf(): Leaf; + /** + * Serializes the preimage into a buffer. + */ toBuffer(): Buffer; + /** + * Serializes the preimage to an array of buffers for hashing. + */ toHashInputs(): Buffer[]; } diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index 617aea4989d7..ecf6f68d14e5 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -3,8 +3,6 @@ import { SiblingPath } from '@aztec/types'; import { AppendOnlyTree } from './append_only_tree.js'; -/* eslint-disable */ - /** * All of the data to be return during batch insertion. */ diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 9e262aa63d77..daa526ee3681 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -10,28 +10,47 @@ import { TreeBase } from '../tree_base.js'; const log = createDebugLogger('aztec:standard-indexed-tree'); -/* eslint-disable */ - -// TODO - -interface Empty { - empty(): T; -} - -interface DummyBuilder { - buildDummy(key: bigint): T; -} - -interface FromBuffer { - fromBuffer(buffer: Buffer): T; -} - -interface Clone { - clone(t: T): T; +/** + * Factory for creating leaf preimages. + */ +export interface PreimageFactory> { + /** + * Creates a new preimage from a leaf. + * @param leaf - Leaf to create a preimage from. + * @param nextKey - Next key of the leaf. + * @param nextIndex - Next index of the leaf. + */ + fromLeaf(leaf: Leaf, nextKey: bigint, nextIndex: bigint): Preimage; + /** + * Creates a new preimage from a buffer. + * @param buffer - Buffer to create a preimage from. + */ + fromBuffer(buffer: Buffer): Preimage; + /** + * Creates an empty preimage. + */ + empty(): Preimage; + /** + * Creates a copy of a preimage. + * @param preimage - Preimage to be cloned. + */ + clone(preimage: Preimage): Preimage; } -interface PreimageFactory> { - fromLeaf(leaf: Leaf, nextKey: bigint, nextIndex: bigint): Preimage; +/** + * Factory for creating leaves. + */ +export interface LeafFactory { + /** + * Creates a new leaf from a buffer. + * @param key - Key of the leaf. + */ + buildDummy(key: bigint): Leaf; + /** + * Creates a new leaf from a buffer. + * @param buffer - Buffer to create a leaf from. + */ + fromBuffer(buffer: Buffer): Leaf; } const leafIndexToDbKey = (name: string, index: bigint) => { @@ -42,6 +61,9 @@ const leafKeyToDbKey = (name: string, key: bigint) => { return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}`; }; +/** + * Standard implementation of an indexed tree. + */ export class StandardIndexedTree> extends TreeBase implements IndexedTree @@ -54,11 +76,8 @@ export class StandardIndexedTree & - Empty & - PreimageFactory & - Clone, - protected leafFactory: FromBuffer & DummyBuilder, + protected leafPreimageFactory: PreimageFactory, + protected leafFactory: LeafFactory, root?: Buffer, ) { super(db, hasher, name, depth, size, root); @@ -66,9 +85,9 @@ export class StandardIndexedTree { + appendLeaves(_leaves: Buffer[]): Promise { throw new Error('Not implemented'); } @@ -103,7 +122,7 @@ export class StandardIndexedTree { + const index = Object.keys(this.cachedLeafPreimages).find(index => { return this.cachedLeafPreimages[index].getKey() === key; }); if (index) { @@ -221,7 +240,7 @@ export class StandardIndexedTree { - let leaf = this.leafFactory.fromBuffer(value); + const leaf = this.leafFactory.fromBuffer(value); let index = await this.db .get(leafKeyToDbKey(this.getName(), leaf.getKey())) .then(data => toBigIntBE(data)) @@ -314,12 +333,9 @@ export class StandardIndexedTree( - treeHeight: N, - preimageFactory: Empty, - ): LowLeafWitnessData { + getEmptyLowLeafWitness(treeHeight: N): LowLeafWitnessData { return { - leafData: preimageFactory.empty(), + leafData: this.leafPreimageFactory.empty(), index: 0n, siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), }; @@ -441,7 +457,7 @@ export class StandardIndexedTree> { - const emptyLowLeafWitness = this.getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); + const emptyLowLeafWitness = this.getEmptyLowLeafWitness(this.getDepth() as TreeHeight); // Accumulators const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); const pendingInsertionSubtree: Preimage[] = leaves.map(() => this.leafPreimageFactory.empty()); From 47bb7f010425efd2dc47d4a84ae256b6c3edc89d Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 09:37:02 +0000 Subject: [PATCH 09/40] refactor: low leaf getter returns option --- .../aztec-node/src/aztec-node/server.ts | 19 +++++------ .../src/interfaces/indexed_tree.ts | 23 +++++++------ .../standard_indexed_tree.ts | 33 ++++++++++--------- .../test/standard_indexed_tree_with_append.ts | 6 ++-- .../src/block_builder/solo_block_builder.ts | 5 +-- .../merkle_tree_operations_facade.ts | 23 +++++++------ .../src/world-state-db/merkle_tree_db.ts | 23 +++++++------ .../src/world-state-db/merkle_trees.ts | 23 +++++++------ 8 files changed, 85 insertions(+), 70 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 53aa86cc19b9..d08034b553a6 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -440,25 +440,24 @@ export class AztecNodeService implements AztecNode { nullifier: Fr, ): Promise { const committedDb = await this.#getWorldState(); - const { index, alreadyPresent } = await committedDb.getPreviousValueIndex( - MerkleTreeId.NULLIFIER_TREE, - nullifier.toBigInt(), - ); + const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + if (!findResult) { + return undefined; + } + const { index, alreadyPresent } = findResult; if (alreadyPresent) { this.log.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); } - const leafData = await committedDb.getLeafPreimage( + const preimageData = (await committedDb.getLeafPreimage( MerkleTreeId.NULLIFIER_TREE, index, - ); - if (!leafData) { - return undefined; - } + ))!; + const siblingPath = await committedDb.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); - return new NullifierMembershipWitness(BigInt(index), leafData, siblingPath); + return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath); } /** diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index ecf6f68d14e5..5dfd4d3c0f12 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -66,16 +66,19 @@ export interface IndexedTree; + ): Promise< + | { + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + } + | undefined + >; /** * Gets the latest LeafPreimage copy. diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index daa526ee3681..6aa4a508da53 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -129,16 +129,19 @@ export class StandardIndexedTree { + ): Promise< + | { + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + } + | undefined + > { let lowLeafIndex = await this.getDbLowLeafIndex(newKey); let lowLeafPreimage = lowLeafIndex !== undefined ? await this.getDbPreimage(lowLeafIndex) : undefined; @@ -153,7 +156,7 @@ export class StandardIndexedTree leafTuple.leaf.toBuffer()), @@ -492,6 +492,9 @@ export class StandardIndexedTree(BigInt(indexOfPrevious.index), true); const witness: LowLeafWitnessData = { diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 047aea52e199..83f17c1aaced 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -42,11 +42,11 @@ export class StandardIndexedTreeWithAppend< } const lowLeafIndex = await this.findIndexOfPreviousKey(newLeaf.getKey(), true); - const lowLeafPreimage = await this.getLatestLeafPreimageCopy(lowLeafIndex.index, true); - - if (lowLeafPreimage === undefined) { + if (lowLeafIndex === undefined) { throw new Error(`Previous leaf not found!`); } + const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(lowLeafIndex.index, true))!; + const newLeafPreimage = this.leafPreimageFactory.fromLeaf( newLeaf, lowLeafPreimage.getNextKey(), diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index e22fe389a7cb..2325a49eb9be 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -576,10 +576,11 @@ export class SoloBlockBuilder implements BlockBuilder { const tree = MerkleTreeId.NULLIFIER_TREE; const prevValueIndex = await this.db.getPreviousValueIndex(tree, frToBigInt(nullifier)); - const prevValuePreimage = await this.db.getLeafPreimage(tree, prevValueIndex.index); - if (!prevValuePreimage) { + if (!prevValueIndex) { throw new Error(`Nullifier tree should have one initial leaf`); } + const prevValuePreimage = (await this.db.getLeafPreimage(tree, prevValueIndex.index))!; + const prevValueSiblingPath = await this.db.getSiblingPath(tree, BigInt(prevValueIndex.index)); return { diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 52b14fb637b6..f00420898abe 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -61,16 +61,19 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { getPreviousValueIndex( treeId: MerkleTreeId.NULLIFIER_TREE, value: bigint, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }> { + ): Promise< + | { + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + } + | undefined + > { return this.trees.getPreviousValueIndex(treeId, value, this.includeUncommitted); } diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index f94808728e7b..ec02c7cc22e0 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -131,16 +131,19 @@ export interface MerkleTreeOperations { getPreviousValueIndex( treeId: IndexedTreeId, value: bigint, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }>; + ): Promise< + | { + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + } + | undefined + >; /** * Returns the data at a specific leaf. diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 1e6a98e43a72..5e1ca83fd6fe 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -316,16 +316,19 @@ export class MerkleTrees implements MerkleTreeDb { treeId: IndexedTreeId, value: bigint, includeUncommitted: boolean, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }> { + ): Promise< + | { + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + } + | undefined + > { return await this.synchronize(() => this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)); } From 7250939625f40b7c960f9045207effd9fd69deb9 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 10:49:16 +0000 Subject: [PATCH 10/40] fix: make sure low leaf in range --- .../src/standard_indexed_tree/standard_indexed_tree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 9d51e6f416a8..d165d3b26a33 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -196,6 +196,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { let lowLeafIndex: bigint | undefined; this.db .createReadStream({ + gte: buildDbKeyForLeafIndex(this.getName(), 0n), lte: buildDbKeyForLeafIndex(this.getName(), key), limit: 1, reverse: true, From 240e8b512407dcfaedbb9f6455562dec6ba1f0fc Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 11:55:12 +0000 Subject: [PATCH 11/40] fix: improve performance of getLeafIndex --- .../merkle-tree/src/snapshots/append_only_snapshot.ts | 11 +++++++++++ .../merkle-tree/src/snapshots/base_full_snapshot.ts | 11 +++++++++++ .../src/snapshots/indexed_tree_snapshot.test.ts | 2 +- .../src/snapshots/indexed_tree_snapshot.ts | 9 ++++++++- .../merkle-tree/src/snapshots/snapshot_builder.ts | 10 +++++++++- .../merkle_tree_snapshot_operations_facade.ts | 9 +-------- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts index b530e981b27e..46361bd5913b 100644 --- a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts @@ -229,4 +229,15 @@ class AppendOnlySnapshot implements TreeSnapshot { return undefined; } } + + async findLeafIndex(value: Buffer): Promise { + const numLeaves = this.getNumLeaves(); + for (let i = 0n; i < numLeaves; i++) { + const currentValue = await this.getLeafValue(i); + if (currentValue && currentValue.equals(value)) { + return i; + } + } + return undefined; + } } diff --git a/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts index 438e77261ff5..b1157cf9d444 100644 --- a/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts @@ -218,4 +218,15 @@ export class BaseFullTreeSnapshot implements TreeSnapshot { path.reverse(); return path; } + + async findLeafIndex(value: Buffer): Promise { + const numLeaves = this.getNumLeaves(); + for (let i = 0n; i < numLeaves; i++) { + const currentValue = await this.getLeafValue(i); + if (currentValue && currentValue.equals(value)) { + return i; + } + } + return undefined; + } } diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts index 3c17d8969ea8..748ea5073278 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts @@ -101,7 +101,7 @@ describe('IndexedTreeSnapshotBuilder', () => { await tree.appendLeaves([Buffer.from('c'), Buffer.from('b'), Buffer.from('e')]); await tree.commit(); - await expect(snapshot.findIndexOfPreviousValue(2n)).resolves.toEqual(historicalPrevValue); + await expect(snapshot.findIndexOfPreviousKey(2n)).resolves.toEqual(historicalPrevValue); }); }); }); diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts index 86e823cbaf3e..006605df283b 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts @@ -59,7 +59,7 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre } } - async findIndexOfPreviousValue(newValue: bigint): Promise<{ + async findIndexOfPreviousKey(newValue: bigint): Promise<{ /** * The index of the found leaf. */ @@ -98,4 +98,11 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre return { index: BigInt(minIndex), alreadyPresent: false }; } + + async findLeafIndex(value: Buffer): Promise { + const index = await this.tree.findLeafIndex(value, false); + if (index && index < this.getNumLeaves()) { + return index; + } + } } diff --git a/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts b/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts index 730d83b261eb..b1fd74f9bdc5 100644 --- a/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts +++ b/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts @@ -48,6 +48,14 @@ export interface TreeSnapshot { * @param index - The index of the leaf for which a sibling path is required. */ getSiblingPath(index: bigint): Promise>; + + /** + * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. + * @param treeId - The ID of the tree. + * @param value - The leaf value to look for. + * @returns The index of the first leaf found with a given value (undefined if not found). + */ + findLeafIndex(value: Buffer): Promise; } /** A snapshot of an indexed tree */ @@ -63,7 +71,7 @@ export interface IndexedTreeSnapshot extends TreeSnapshot { * @param newValue - The new value to be inserted into the tree. * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`. */ - findIndexOfPreviousValue(newValue: bigint): Promise<{ + findIndexOfPreviousKey(newValue: bigint): Promise<{ /** * The index of the found leaf. */ diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts index 83420c687a22..ef112c30e35f 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts @@ -29,14 +29,7 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations async findLeafIndex(treeId: MerkleTreeId, value: Buffer): Promise { const tree = await this.#getTreeSnapshot(treeId); - const numLeaves = tree.getNumLeaves(); - for (let i = 0n; i < numLeaves; i++) { - const currentValue = await tree.getLeafValue(i); - if (currentValue && currentValue.equals(value)) { - return i; - } - } - return undefined; + return tree.findLeafIndex(value); } getLatestGlobalVariablesHash(): Promise { From bcc8ae0e5eddaa7bc38d0560bd20513129ee9dd8 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 12:37:49 +0000 Subject: [PATCH 12/40] chore: cleanups --- .../aztec-node/src/aztec-node/server.ts | 8 ++-- .../src/interfaces/indexed_tree.ts | 2 +- .../src/snapshots/indexed_tree_snapshot.ts | 10 ++--- .../snapshots/snapshot_builder_test_suite.ts | 20 +++++++++ .../standard_indexed_tree.ts | 11 ++--- .../merkle-tree/src/test/test_suite.ts | 14 ++++++ yarn-project/merkle-tree/src/tree_base.ts | 1 - yarn-project/noir-contracts/scripts/types.sh | 45 +++++++++---------- .../noir-protocol-circuits/src/index.ts | 1 - .../src/type_conversion.ts | 3 -- .../src/block_builder/solo_block_builder.ts | 4 +- .../types/src/interfaces/nullifier_tree.ts | 8 ++-- .../merkle_tree_operations_facade.ts | 2 +- .../src/world-state-db/merkle_trees.ts | 2 +- 14 files changed, 79 insertions(+), 52 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 46c218cbcf91..bc415839d7b9 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -430,19 +430,19 @@ export class AztecNodeService implements AztecNode { return undefined; } - const leafDataPromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index); + const leafPreimagePromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index); const siblingPathPromise = db.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); - const [leafData, siblingPath] = await Promise.all([leafDataPromise, siblingPathPromise]); + const [leafPreimage, siblingPath] = await Promise.all([leafPreimagePromise, siblingPathPromise]); - if (!leafData) { + if (!leafPreimage) { return undefined; } - return new NullifierMembershipWitness(BigInt(index), leafData as NullifierLeafPreimage, siblingPath); + return new NullifierMembershipWitness(BigInt(index), leafPreimage as NullifierLeafPreimage, siblingPath); } /** diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index 4dac599fd51b..eee22a3ee2a0 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -10,7 +10,7 @@ export interface LowLeafWitnessData { /** * Preimage of the low nullifier that proves non membership. */ - leafData: IndexedTreeLeafPreimage; + leafPreimage: IndexedTreeLeafPreimage; /** * Sibling path to prove membership of low nullifier. */ diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts index 006605df283b..98e5373537c2 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts @@ -25,9 +25,9 @@ export class IndexedTreeSnapshotBuilder } protected async handleLeaf(index: bigint, node: Buffer, batch: LevelUpChain) { - const leafData = await this.tree.getLatestLeafPreimageCopy(index, false); - if (leafData) { - batch.put(snapshotLeafValue(node, index), leafData.toBuffer()); + const leafPreimage = await this.tree.getLatestLeafPreimageCopy(index, false); + if (leafPreimage) { + batch.put(snapshotLeafValue(node, index), leafPreimage.toBuffer()); } } } @@ -45,8 +45,8 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre } async getLeafValue(index: bigint): Promise { - const leafData = await this.getLatestLeafPreimageCopy(index); - return leafData?.toBuffer(); + const leafPreimage = await this.getLatestLeafPreimageCopy(index); + return leafPreimage?.toBuffer(); } async getLatestLeafPreimageCopy(index: bigint): Promise { diff --git a/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts b/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts index 227919645b1f..f50ff1d69ae9 100644 --- a/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts +++ b/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts @@ -194,5 +194,25 @@ export function describeSnapshotBuilderTestSuite { + it('returns the historical leaf index when the snapshot was taken', async () => { + await modifyTree(tree); + await tree.commit(); + const snapshot = await snapshotBuilder.snapshot(1); + + const initialLastLeafIndex = tree.getNumLeaves(false) - 1n; + let lastLeaf = await tree.getLeafValue(initialLastLeafIndex, false); + expect(await snapshot.findLeafIndex(lastLeaf!)).toBe(initialLastLeafIndex); + + await modifyTree(tree); + await tree.commit(); + + const newLastLeafIndex = tree.getNumLeaves(false) - 1n; + lastLeaf = await tree.getLeafValue(newLastLeafIndex, false); + + expect(await snapshot.findLeafIndex(lastLeaf!)).toBe(undefined); + }); + }); }); } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index d165d3b26a33..a935bcef75ef 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -88,8 +88,10 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { } /** - * Appends a set of leaf values to the tree. - * @param _leaves - The set of leaves to be appended. + * Appends the given leaves to the tree. + * @param _leaves - The leaves to append. + * @returns Empty promise. + * @remarks Use batchInsert method instead. */ appendLeaves(_leaves: Buffer[]): Promise { throw new Error('Not implemented'); @@ -245,7 +247,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { /** * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. - * @param treeId - The ID of the tree. * @param value - The leaf value to look for. * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). @@ -346,7 +347,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { */ getEmptyLowLeafWitness(treeHeight: N): LowLeafWitnessData { return { - leafData: this.leafPreimageFactory.empty(), + leafPreimage: this.leafPreimageFactory.empty(), index: 0n, siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), }; @@ -506,7 +507,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); const witness: LowLeafWitnessData = { - leafData: lowLeafPreimage, + leafPreimage: lowLeafPreimage, index: BigInt(indexOfPrevious.index), siblingPath, }; diff --git a/yarn-project/merkle-tree/src/test/test_suite.ts b/yarn-project/merkle-tree/src/test/test_suite.ts index 1940f32ce913..51886a090b3d 100644 --- a/yarn-project/merkle-tree/src/test/test_suite.ts +++ b/yarn-project/merkle-tree/src/test/test_suite.ts @@ -157,5 +157,19 @@ export const treeTestSuite = ( expect(deserialized2.elem).toEqual(siblingPath); expect(deserialized2.adv).toBe(4 + 10 * 32); }); + + it('should be able to find indexes of leaves', async () => { + const db = levelup(createMemDown()); + const tree = await createDb(db, pedersen, 'test', 10); + await appendLeaves(tree, values.slice(0, 1)); + + expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); + expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); + expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); + + await tree.commit(); + + expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); + }); }); }; diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index b3182abd5ba4..f9f58068cbe2 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -303,7 +303,6 @@ export abstract class TreeBase implements MerkleTree { /** * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. - * @param treeId - The ID of the tree. * @param value - The leaf value to look for. * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). diff --git a/yarn-project/noir-contracts/scripts/types.sh b/yarn-project/noir-contracts/scripts/types.sh index 2747a38ef9b3..14fa63bfcd16 100755 --- a/yarn-project/noir-contracts/scripts/types.sh +++ b/yarn-project/noir-contracts/scripts/types.sh @@ -1,4 +1,3 @@ - #!/bin/bash # Example: @@ -8,7 +7,7 @@ # Enable strict mode: # Exit on error (set -e), treat unset variables as an error (set -u), -set -eu; +set -eu artifacts_dir="src/artifacts" types_dir="src/types" @@ -17,42 +16,40 @@ types_dir="src/types" mkdir -p $types_dir mkdir -p $artifacts_dir - ROOT=$(pwd) write_import() { - CONTRACT_NAME=$1 - NAME=$(echo $CONTRACT_NAME | perl -pe 's/(^|_)(\w)/\U$2/g') + CONTRACT_NAME=$1 + NAME=$(echo $CONTRACT_NAME | perl -pe 's/(^|_)(\w)/\U$2/g') - echo "import ${NAME}Json from './${CONTRACT_NAME}_contract.json' assert { type: 'json' };" >> "$artifacts_dir/index.ts"; + echo "import ${NAME}Json from './${CONTRACT_NAME}_contract.json' assert { type: 'json' };" >>"$artifacts_dir/index.ts" } write_export() { - CONTRACT_NAME=$1 - NAME=$(echo $CONTRACT_NAME | perl -pe 's/(^|_)(\w)/\U$2/g') + CONTRACT_NAME=$1 + NAME=$(echo $CONTRACT_NAME | perl -pe 's/(^|_)(\w)/\U$2/g') - # artifacts - echo "export const ${NAME}ContractArtifact = ${NAME}Json as ContractArtifact;" >> "$artifacts_dir/index.ts"; - echo "Written typescript for $NAME" + # artifacts + echo "export const ${NAME}ContractArtifact = ${NAME}Json as ContractArtifact;" >>"$artifacts_dir/index.ts" + echo "Written typescript for $NAME" - # types - echo "export * from './${CONTRACT_NAME}.js';" >> "$types_dir/index.ts"; + # types + echo "export * from './${CONTRACT_NAME}.js';" >>"$types_dir/index.ts" } - process() { CONTRACT=$1 cd $ROOT - NODE_OPTIONS=--no-warnings yarn ts-node --esm src/scripts/copy_source.ts $CONTRACT_NAME + NODE_OPTIONS="--no-warnings --loader ts-node/esm" yarn ts-node --esm src/scripts/copy_source.ts $CONTRACT_NAME echo "Creating types for $CONTRACT" - NODE_OPTIONS=--no-warnings yarn ts-node --esm src/scripts/copy_output.ts $CONTRACT_NAME + NODE_OPTIONS="--no-warnings --loader ts-node/esm" yarn ts-node --esm src/scripts/copy_output.ts $CONTRACT_NAME } -format(){ +format() { echo "Formatting contract folders" - yarn run -T prettier -w ../aztec.js/src/artifacts/*.json ./$types_dir/*.ts + yarn run -T prettier -w ../aztec.js/src/artifacts/*.json ./$types_dir/*.ts echo -e "Done\n" } @@ -69,15 +66,15 @@ wait rm -f $artifacts_dir/index.ts || true # Generate artifacts package index.ts -echo "// Auto generated module\n" > "$artifacts_dir/index.ts"; -echo "import { ContractArtifact } from '@aztec/foundation/abi';" >> "$artifacts_dir/index.ts"; +echo "// Auto generated module\n" >"$artifacts_dir/index.ts" +echo "import { ContractArtifact } from '@aztec/foundation/abi';" >>"$artifacts_dir/index.ts" # Generate types package index.ts -echo "// Auto generated module\n" > "$types_dir/index.ts"; +echo "// Auto generated module\n" >"$types_dir/index.ts" for CONTRACT_NAME in "$@"; do - write_import $CONTRACT_NAME - write_export $CONTRACT_NAME + write_import $CONTRACT_NAME + write_export $CONTRACT_NAME done # only run the rest when the full flag is set -format +format \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/index.ts b/yarn-project/noir-protocol-circuits/src/index.ts index dc5baffd5fd3..97671183a7c6 100644 --- a/yarn-project/noir-protocol-circuits/src/index.ts +++ b/yarn-project/noir-protocol-circuits/src/index.ts @@ -416,7 +416,6 @@ async function executeMergeRollupWithACVM(input: MergeRollupInputType): Promise< * Executes the base rollup with the given inputs using the acvm. */ async function executeBaseRollupWithACVM(input: BaseRollupInputType): Promise { - // console.log(JSON.stringify(input, null, 4)); const initialWitnessMap = abiEncode(BaseRollupJson.abi as Abi, input as any); // Execute the circuit on those initial witness values diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index d0e49d71442a..3ff307a60344 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -163,9 +163,6 @@ export function mapNumberFromNoir(number: NoirField): number { * */ export function mapNumberToNoir(number: number): NoirField { - if (number > 2 ** 32) { - throw new Error('Number out of range'); - } return new Fr(BigInt(number)).toString(); } diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index b1602f5d93c5..f160b8dffe4c 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -708,7 +708,7 @@ export class SoloBlockBuilder implements BlockBuilder { newSubtreeSiblingPath: newNullifiersSubtreeSiblingPath, sortedNewLeaves: sortedNewNullifiers, sortedNewLeavesIndexes: sortednewNullifiersIndexes, - } = await this.db.batchInsert( + } = await this.db.batchInsert( MerkleTreeId.NULLIFIER_TREE, newNullifiers.map(fr => fr.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, @@ -743,7 +743,7 @@ export class SoloBlockBuilder implements BlockBuilder { newPublicDataReadsSiblingPaths, lowNullifierLeafPreimages: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i < nullifierWitnessLeaves.length - ? (nullifierWitnessLeaves[i].leafData as NullifierLeafPreimage) + ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage) : NullifierLeafPreimage.empty(), ), lowNullifierMembershipWitness: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => diff --git a/yarn-project/types/src/interfaces/nullifier_tree.ts b/yarn-project/types/src/interfaces/nullifier_tree.ts index ab3053ef858c..14fdf426b8d0 100644 --- a/yarn-project/types/src/interfaces/nullifier_tree.ts +++ b/yarn-project/types/src/interfaces/nullifier_tree.ts @@ -17,7 +17,7 @@ export class NullifierMembershipWitness { /** * Preimage of the nullifier. */ - public readonly leafData: NullifierLeafPreimage, + public readonly leafPreimage: NullifierLeafPreimage, /** * Sibling path to prove membership of the nullifier. */ @@ -31,9 +31,9 @@ export class NullifierMembershipWitness { public toFieldArray(): Fr[] { return [ new Fr(this.index), - new Fr(this.leafData.nullifier), - new Fr(this.leafData.nextIndex), - new Fr(this.leafData.nextNullifier), + new Fr(this.leafPreimage.nullifier), + new Fr(this.leafPreimage.nextIndex), + new Fr(this.leafPreimage.nextNullifier), ...this.siblingPath.toFieldArray(), ]; } diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 05a257912466..3b7f4c134549 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -92,7 +92,7 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { * Gets the leaf data at a given index and tree. * @param treeId - The ID of the tree get the leaf from. * @param index - The index of the leaf to get. - * @returns Leaf data. + * @returns Leaf preimage. */ async getLeafPreimage( treeId: MerkleTreeId.NULLIFIER_TREE, diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index bf8bf02d3627..9a7f1ad629e2 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -337,7 +337,7 @@ export class MerkleTrees implements MerkleTreeDb { * @param treeId - The ID of the tree get the leaf from. * @param index - The index of the leaf to get. * @param includeUncommitted - Indicates whether to include uncommitted data. - * @returns Leaf data. + * @returns Leaf preimage. */ public async getLeafPreimage( treeId: IndexedTreeId, From 1b7c9f88b6327fa8978201c7ee1b61770bd96a09 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 12:54:45 +0000 Subject: [PATCH 13/40] style: fix formatting --- yarn-project/circuits.js/src/structs/rollup/base_rollup.ts | 3 +-- .../src/merkle-tree/merkle_tree_operations_facade.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts index 78bf7c054e2c..0ff774198420 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts @@ -1,8 +1,7 @@ import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, Tuple } from '@aztec/foundation/serialize'; -import { IndexedTreeLeaf } from '@aztec/foundation/trees'; -import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BLOCKS_TREE_HEIGHT, diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 3b7f4c134549..494040e381ea 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,6 +1,6 @@ import { NullifierLeafPreimage } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; -import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; From 08791c53d89df9d706cbe2090b4e20ef6c2ad031 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 14:12:05 +0000 Subject: [PATCH 14/40] chore: reset file to master --- yarn-project/noir-contracts/scripts/types.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/noir-contracts/scripts/types.sh b/yarn-project/noir-contracts/scripts/types.sh index 14fa63bfcd16..c25603dda38a 100755 --- a/yarn-project/noir-contracts/scripts/types.sh +++ b/yarn-project/noir-contracts/scripts/types.sh @@ -77,4 +77,4 @@ for CONTRACT_NAME in "$@"; do done # only run the rest when the full flag is set -format \ No newline at end of file +format From 678bc75154ba40d5ef345953ab735580b83f4ba9 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 17:24:58 +0000 Subject: [PATCH 15/40] feat: public data tree switch --- .../aztec-node/src/aztec-node/server.ts | 8 +- .../aztec-nr/aztec/src/constants_gen.nr | 4 +- yarn-project/circuits.js/src/constants.gen.ts | 4 +- .../kernel/combined_accumulated_data.ts | 12 +- .../src/structs/rollup/base_rollup.ts | 178 +- .../structs/rollup/nullifier_leaf/index.ts | 111 ++ .../structs/rollup/public_data_leaf/index.ts | 126 ++ .../circuits.js/src/tests/factories.ts | 142 +- .../end-to-end/src/e2e_card_game.test.ts | 2 +- yarn-project/foundation/src/trees/index.ts | 6 + .../standard_indexed_tree.ts | 43 +- .../crates/public-kernel-lib/src/common.nr | 4 +- .../src/crates/public-kernel-lib/src/utils.nr | 4 +- .../src/crates/rollup-lib/src/abis.nr | 1 + .../src/abis/public_data_tree_leaf.nr | 49 + .../rollup-lib/src/base/base_rollup_inputs.nr | 1611 +++++++++-------- .../types/src/abis/membership_witness.nr | 6 + .../crates/types/src/abis/public_data_read.nr | 10 +- .../src/abis/public_data_update_request.nr | 10 +- .../src/tests/previous_kernel_data_builder.nr | 4 +- .../src/scripts/generate_ts_from_abi.ts | 6 +- .../src/type_conversion.ts | 116 +- .../src/types/private_kernel_init_types.ts | 4 +- .../src/types/private_kernel_inner_types.ts | 4 +- .../types/private_kernel_ordering_types.ts | 4 +- .../public_kernel_private_previous_types.ts | 4 +- .../public_kernel_public_previous_types.ts | 4 +- .../src/types/rollup_base_types.ts | 30 +- .../block_builder/solo_block_builder.test.ts | 4 +- .../src/block_builder/solo_block_builder.ts | 165 +- .../src/sequencer/public_processor.ts | 8 +- .../src/world-state-db/merkle_tree_db.ts | 9 +- .../src/world-state-db/merkle_trees.ts | 14 +- 33 files changed, 1580 insertions(+), 1127 deletions(-) create mode 100644 yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts create mode 100644 yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts create mode 100644 yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index bc415839d7b9..a3c5cfdf80cc 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -493,8 +493,12 @@ export class AztecNodeService implements AztecNode { */ public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { const committedDb = await this.#getWorldState('latest'); - const leafIndex = computePublicDataTreeIndex(contract, slot); - const value = await committedDb.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex.value); + const leafSlot = computePublicDataTreeIndex(contract, slot); + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult?.alreadyPresent) { + return undefined; + } + const value = await committedDb.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); return value ? Fr.fromBuffer(value) : undefined; } diff --git a/yarn-project/aztec-nr/aztec/src/constants_gen.nr b/yarn-project/aztec-nr/aztec/src/constants_gen.nr index d1d77e5b81c9..50aa16c78d08 100644 --- a/yarn-project/aztec-nr/aztec/src/constants_gen.nr +++ b/yarn-project/aztec-nr/aztec/src/constants_gen.nr @@ -62,7 +62,7 @@ global VK_TREE_HEIGHT: Field = 3; global FUNCTION_TREE_HEIGHT: Field = 5; global CONTRACT_TREE_HEIGHT: Field = 16; global NOTE_HASH_TREE_HEIGHT: Field = 32; -global PUBLIC_DATA_TREE_HEIGHT: Field = 254; +global PUBLIC_DATA_TREE_HEIGHT: Field = 40; global NULLIFIER_TREE_HEIGHT: Field = 20; global L1_TO_L2_MSG_TREE_HEIGHT: Field = 16; global ROLLUP_VK_TREE_HEIGHT: Field = 8; @@ -73,8 +73,10 @@ global CONTRACT_SUBTREE_SIBLING_PATH_LENGTH: Field = 15; global NOTE_HASH_SUBTREE_HEIGHT: Field = 7; global NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH: Field = 25; global NULLIFIER_SUBTREE_HEIGHT: Field = 7; +global PUBLIC_DATA_SUBTREE_HEIGHT: Field = 4; global BLOCKS_TREE_HEIGHT: Field = 16; global NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH: Field = 13; +global PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH: Field = 36; global L1_TO_L2_MSG_SUBTREE_HEIGHT: Field = 4; global L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH: Field = 12; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 89b201b5e5ce..0a9367c62fd7 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -29,11 +29,13 @@ export const KERNELS_PER_BASE_ROLLUP = 2; export const MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP = 128; export const MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP = 32; export const MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP = 32; +export const PUBLIC_DATA_SUBTREE_HEIGHT = 4; export const VK_TREE_HEIGHT = 3; export const FUNCTION_TREE_HEIGHT = 5; export const CONTRACT_TREE_HEIGHT = 16; export const NOTE_HASH_TREE_HEIGHT = 32; -export const PUBLIC_DATA_TREE_HEIGHT = 254; +export const PUBLIC_DATA_TREE_HEIGHT = 40; +export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 36; export const NULLIFIER_TREE_HEIGHT = 20; export const L1_TO_L2_MSG_TREE_HEIGHT = 16; export const ROLLUP_VK_TREE_HEIGHT = 8; diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index f77a3d910c44..3dff253c15cd 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -180,7 +180,7 @@ export class PublicDataRead { /** * Index of the leaf in the public data tree. */ - public readonly leafIndex: Fr, + public readonly leafSlot: Fr, /** * Returned value from the public data tree. */ @@ -205,7 +205,7 @@ export class PublicDataRead { } toBuffer() { - return serializeToBuffer(this.leafIndex, this.value); + return serializeToBuffer(this.leafSlot, this.value); } static fromBuffer(buffer: Buffer | BufferReader) { @@ -218,7 +218,7 @@ export class PublicDataRead { } toFriendlyJSON() { - return `Leaf=${this.leafIndex.toFriendlyJSON()}: ${this.value.toFriendlyJSON()}`; + return `Leaf=${this.leafSlot.toFriendlyJSON()}: ${this.value.toFriendlyJSON()}`; } } @@ -230,7 +230,7 @@ export class PublicDataUpdateRequest { /** * Index of the leaf in the public data tree which is to be updated. */ - public readonly leafIndex: Fr, + public readonly leafSlot: Fr, /** * Old value of the leaf. */ @@ -263,7 +263,7 @@ export class PublicDataUpdateRequest { } toBuffer() { - return serializeToBuffer(this.leafIndex, this.oldValue, this.newValue); + return serializeToBuffer(this.leafSlot, this.oldValue, this.newValue); } static fromBuffer(buffer: Buffer | BufferReader) { @@ -276,7 +276,7 @@ export class PublicDataUpdateRequest { } toFriendlyJSON() { - return `Leaf=${this.leafIndex.toFriendlyJSON()}: ${this.oldValue.toFriendlyJSON()} => ${this.newValue.toFriendlyJSON()}`; + return `Leaf=${this.leafSlot.toFriendlyJSON()}: ${this.oldValue.toFriendlyJSON()} => ${this.newValue.toFriendlyJSON()}`; } } diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts index 0ff774198420..3de5dac8433b 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts @@ -1,18 +1,17 @@ -import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, Tuple } from '@aztec/foundation/serialize'; -import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BLOCKS_TREE_HEIGHT, CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, KERNELS_PER_BASE_ROLLUP, MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, + MAX_PUBLIC_DATA_READS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, } from '../../constants.gen.js'; import { FieldsOf } from '../../utils/jsUtils.js'; @@ -22,110 +21,10 @@ import { PreviousKernelData } from '../kernel/previous_kernel_data.js'; import { MembershipWitness } from '../membership_witness.js'; import { UInt32 } from '../shared.js'; import { AppendOnlyTreeSnapshot } from './append_only_tree_snapshot.js'; +import { NullifierLeaf, NullifierLeafPreimage } from './nullifier_leaf/index.js'; +import { PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from './public_data_leaf/index.js'; -/** - * Class containing the data of a preimage of a single leaf in the nullifier tree. - * Note: It's called preimage because this data gets hashed before being inserted as a node into the `IndexedTree`. - */ -export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { - constructor( - /** - * Leaf value inside the indexed tree's linked list. - */ - public nullifier: Fr, - /** - * Next value inside the indexed tree's linked list. - */ - public nextNullifier: Fr, - /** - * Index of the next leaf in the indexed tree's linked list. - */ - public nextIndex: bigint, - ) {} - - getKey(): bigint { - return this.nullifier.toBigInt(); - } - - getNextKey(): bigint { - return this.nextNullifier.toBigInt(); - } - - getNextIndex(): bigint { - return this.nextIndex; - } - - asLeaf(): NullifierLeaf { - return new NullifierLeaf(this.nullifier); - } - - toBuffer(): Buffer { - return Buffer.concat(this.toHashInputs()); - } - - toHashInputs(): Buffer[] { - return [ - Buffer.from(this.nullifier.toBuffer()), - Buffer.from(toBufferBE(this.nextIndex, 32)), - Buffer.from(this.nextNullifier.toBuffer()), - ]; - } - - clone(): NullifierLeafPreimage { - return new NullifierLeafPreimage(this.nullifier, this.nextNullifier, this.nextIndex); - } - - static empty(): NullifierLeafPreimage { - return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0n); - } - - static fromBuffer(buf: Buffer): NullifierLeafPreimage { - const nullifier = Fr.fromBuffer(buf.subarray(0, 32)); - const nextIndex = toBigIntBE(buf.subarray(32, 64)); - const nextNullifier = Fr.fromBuffer(buf.subarray(64, 96)); - return new NullifierLeafPreimage(nullifier, nextNullifier, nextIndex); - } - - static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { - return new NullifierLeafPreimage(leaf.nullifier, new Fr(nextKey), nextIndex); - } - - static clone(preimage: NullifierLeafPreimage): NullifierLeafPreimage { - return new NullifierLeafPreimage(preimage.nullifier, preimage.nextNullifier, preimage.nextIndex); - } -} - -/** - * A nullifier to be inserted in the nullifier tree. - */ -export class NullifierLeaf implements IndexedTreeLeaf { - constructor( - /** - * Nullifier value. - */ - public nullifier: Fr, - ) {} - - getKey(): bigint { - return this.nullifier.toBigInt(); - } - - toBuffer(): Buffer { - return this.nullifier.toBuffer(); - } - - isEmpty(): boolean { - return this.nullifier.isZero(); - } - - static buildDummy(key: bigint): NullifierLeaf { - return new NullifierLeaf(new Fr(key)); - } - - static fromBuffer(buf: Buffer): NullifierLeaf { - return new NullifierLeaf(Fr.fromBuffer(buf)); - } -} +export { NullifierLeaf, NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage }; /** * Data which is forwarded through the base rollup circuits unchanged. @@ -242,6 +141,7 @@ export class BaseRollupInputs { MembershipWitness, typeof MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP >, + /** * Sibling path "pointing to" where the new commitments subtree should be inserted into the note hash tree. */ @@ -255,21 +155,60 @@ export class BaseRollupInputs { */ public newContractsSubtreeSiblingPath: Tuple, /** - * Sibling paths of leaves which are to be affected by the public data update requests. - * Each item in the array is the sibling path that corresponds to an update request. + * The public data writes to be inserted in the tree, sorted high slot to low slot. + */ + public sortedPublicDataWrites: Tuple< + Tuple, + typeof KERNELS_PER_BASE_ROLLUP + >, + /** + * The indexes of the sorted public data writes to the original ones. + */ + public sortedPublicDataWritesIndexes: Tuple< + Tuple, + typeof KERNELS_PER_BASE_ROLLUP + >, + /** + * The public data writes which need to be updated to perform the batch insertion of the new public data writes. + * See `StandardIndexedTree.batchInsert` function for more details. + */ + public lowPublicDataWritesPreimages: Tuple< + Tuple, + typeof KERNELS_PER_BASE_ROLLUP + >, + /** + * Membership witnesses for the nullifiers which need to be updated to perform the batch insertion of the new + * nullifiers. + */ + public lowPublicDataWritesMembershipWitnesses: Tuple< + Tuple, typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, + typeof KERNELS_PER_BASE_ROLLUP + >, + + /** + * Sibling path "pointing to" where the new public data subtree should be inserted into the public data tree. + */ + public publicDataWritesSubtreeSiblingPaths: Tuple< + Tuple, + typeof KERNELS_PER_BASE_ROLLUP + >, + + /** + * Preimages of leaves which are to be read by the public data reads. */ - public newPublicDataUpdateRequestsSiblingPaths: Tuple< - Tuple, - typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP + public publicDataReadsPreimages: Tuple< + Tuple, + typeof KERNELS_PER_BASE_ROLLUP >, /** * Sibling paths of leaves which are to be read by the public data reads. * Each item in the array is the sibling path that corresponds to a read request. */ - public newPublicDataReadsSiblingPaths: Tuple< - Tuple, - typeof MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP + public publicDataReadsMembershipWitnesses: Tuple< + Tuple, typeof MAX_PUBLIC_DATA_READS_PER_TX>, + typeof KERNELS_PER_BASE_ROLLUP >, + /** * Membership witnesses of blocks referred by each of the 2 kernels. */ @@ -302,8 +241,13 @@ export class BaseRollupInputs { fields.newCommitmentsSubtreeSiblingPath, fields.newNullifiersSubtreeSiblingPath, fields.newContractsSubtreeSiblingPath, - fields.newPublicDataUpdateRequestsSiblingPaths, - fields.newPublicDataReadsSiblingPaths, + fields.sortedPublicDataWrites, + fields.sortedPublicDataWritesIndexes, + fields.lowPublicDataWritesPreimages, + fields.lowPublicDataWritesMembershipWitnesses, + fields.publicDataWritesSubtreeSiblingPaths, + fields.publicDataReadsPreimages, + fields.publicDataReadsMembershipWitnesses, fields.blocksTreeRootMembershipWitnesses, fields.constants, ] as const; diff --git a/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts b/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts new file mode 100644 index 000000000000..7be3c23d1a2d --- /dev/null +++ b/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts @@ -0,0 +1,111 @@ +import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { Fr } from '@aztec/foundation/fields'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; + +/** + * Class containing the data of a preimage of a single leaf in the nullifier tree. + * Note: It's called preimage because this data gets hashed before being inserted as a node into the `IndexedTree`. + */ +export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { + constructor( + /** + * Leaf value inside the indexed tree's linked list. + */ + public nullifier: Fr, + /** + * Next value inside the indexed tree's linked list. + */ + public nextNullifier: Fr, + /** + * Index of the next leaf in the indexed tree's linked list. + */ + public nextIndex: bigint, + ) {} + + getKey(): bigint { + return this.nullifier.toBigInt(); + } + + getNextKey(): bigint { + return this.nextNullifier.toBigInt(); + } + + getNextIndex(): bigint { + return this.nextIndex; + } + + asLeaf(): NullifierLeaf { + return new NullifierLeaf(this.nullifier); + } + + toBuffer(): Buffer { + return Buffer.concat(this.toHashInputs()); + } + + toHashInputs(): Buffer[] { + return [ + Buffer.from(this.nullifier.toBuffer()), + Buffer.from(toBufferBE(this.nextIndex, 32)), + Buffer.from(this.nextNullifier.toBuffer()), + ]; + } + + clone(): NullifierLeafPreimage { + return new NullifierLeafPreimage(this.nullifier, this.nextNullifier, this.nextIndex); + } + + static empty(): NullifierLeafPreimage { + return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0n); + } + + static fromBuffer(buf: Buffer): NullifierLeafPreimage { + const nullifier = Fr.fromBuffer(buf.subarray(0, 32)); + const nextIndex = toBigIntBE(buf.subarray(32, 64)); + const nextNullifier = Fr.fromBuffer(buf.subarray(64, 96)); + return new NullifierLeafPreimage(nullifier, nextNullifier, nextIndex); + } + + static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { + return new NullifierLeafPreimage(leaf.nullifier, new Fr(nextKey), nextIndex); + } + + static clone(preimage: NullifierLeafPreimage): NullifierLeafPreimage { + return new NullifierLeafPreimage(preimage.nullifier, preimage.nextNullifier, preimage.nextIndex); + } +} + +/** + * A nullifier to be inserted in the nullifier tree. + */ +export class NullifierLeaf implements IndexedTreeLeaf { + constructor( + /** + * Nullifier value. + */ + public nullifier: Fr, + ) {} + + getKey(): bigint { + return this.nullifier.toBigInt(); + } + + toBuffer(): Buffer { + return this.nullifier.toBuffer(); + } + + isEmpty(): boolean { + return this.nullifier.isZero(); + } + + updateTo(_another: NullifierLeaf): void { + throw new Error('Nullifiers are create only'); + } + + static buildDummy(key: bigint): NullifierLeaf { + return new NullifierLeaf(new Fr(key)); + } + + static fromBuffer(buf: Buffer): NullifierLeaf { + return new NullifierLeaf(Fr.fromBuffer(buf)); + } +} diff --git a/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts b/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts new file mode 100644 index 000000000000..a7451d832dab --- /dev/null +++ b/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts @@ -0,0 +1,126 @@ +import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader } from '@aztec/foundation/serialize'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; + +import { serializeToBuffer } from '../../../utils/serialize.js'; + +/** + * Class containing the data of a preimage of a single leaf in the public data tree. + * Note: It's called preimage because this data gets hashed before being inserted as a node into the `IndexedTree`. + */ +export class PublicDataTreeLeafPreimage implements IndexedTreeLeafPreimage { + constructor( + /** + * The slot of the leaf + */ + public slot: Fr, + /** + * The value of the leaf + */ + public value: Fr, + /** + * Next value inside the indexed tree's linked list. + */ + public nextSlot: Fr, + /** + * Index of the next leaf in the indexed tree's linked list. + */ + public nextIndex: bigint, + ) {} + + getKey(): bigint { + return this.slot.toBigInt(); + } + + getNextKey(): bigint { + return this.nextSlot.toBigInt(); + } + + getNextIndex(): bigint { + return this.nextIndex; + } + + asLeaf(): PublicDataTreeLeaf { + return new PublicDataTreeLeaf(this.slot, this.value); + } + + toBuffer(): Buffer { + return Buffer.concat(this.toHashInputs()); + } + + toHashInputs(): Buffer[] { + return [ + Buffer.from(this.slot.toBuffer()), + Buffer.from(this.value.toBuffer()), + Buffer.from(toBufferBE(this.nextIndex, 32)), + Buffer.from(this.nextSlot.toBuffer()), + ]; + } + + clone(): PublicDataTreeLeafPreimage { + return new PublicDataTreeLeafPreimage(this.slot, this.value, this.nextSlot, this.nextIndex); + } + + static empty(): PublicDataTreeLeafPreimage { + return new PublicDataTreeLeafPreimage(Fr.ZERO, Fr.ZERO, Fr.ZERO, 0n); + } + + static fromBuffer(buffer: Buffer): PublicDataTreeLeafPreimage { + const reader = BufferReader.asReader(buffer); + const slot = Fr.fromBuffer(reader); + const value = Fr.fromBuffer(reader); + const nextIndex = toBigIntBE(reader.readBytes(32)); + const nextSlot = Fr.fromBuffer(reader); + return new PublicDataTreeLeafPreimage(slot, value, nextSlot, nextIndex); + } + + static fromLeaf(leaf: PublicDataTreeLeaf, nextKey: bigint, nextIndex: bigint): PublicDataTreeLeafPreimage { + return new PublicDataTreeLeafPreimage(leaf.slot, leaf.value, new Fr(nextKey), nextIndex); + } + + static clone(preimage: PublicDataTreeLeafPreimage): PublicDataTreeLeafPreimage { + return new PublicDataTreeLeafPreimage(preimage.slot, preimage.value, preimage.nextSlot, preimage.nextIndex); + } +} + +/** + * A leaf in the public data indexed tree. + */ +export class PublicDataTreeLeaf implements IndexedTreeLeaf { + constructor( + /** + * The slot the value is stored in + */ + public slot: Fr, + /** + * The value stored in the slot + */ + public value: Fr, + ) {} + + getKey(): bigint { + return this.slot.toBigInt(); + } + + toBuffer() { + return serializeToBuffer([this.slot, this.value]); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PublicDataTreeLeaf(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); + } + + isEmpty(): boolean { + return this.slot.isZero() && this.value.isZero(); + } + + updateTo(another: PublicDataTreeLeaf): void { + this.value = another.value; + } + + static buildDummy(key: bigint): PublicDataTreeLeaf { + return new PublicDataTreeLeaf(new Fr(key), new Fr(0)); + } +} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index a2b1011d3652..19258cea48ec 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -890,77 +890,77 @@ export function makeMergeRollupInputs(seed = 0): MergeRollupInputs { return new MergeRollupInputs([makePreviousRollupData(seed), makePreviousRollupData(seed + 0x1000)]); } -/** - * Makes arbitrary base rollup inputs. - * @param seed - The seed to use for generating the base rollup inputs. - * @returns A base rollup inputs. - */ -export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { - const kernelData = makeTuple(KERNELS_PER_BASE_ROLLUP, x => makePreviousKernelData(seed + (x + 1) * 0x100)); - - const startNoteHashTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x100); - const startNullifierTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x200); - const startContractTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x300); - const startPublicDataTreeRoot = fr(seed + 0x400); - const startBlocksTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x500); - - const lowNullifierLeafPreimages = makeTuple( - MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), - seed + 0x1000, - ); - - const lowNullifierMembershipWitness = makeTuple( - MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - x => makeMembershipWitness(NULLIFIER_TREE_HEIGHT, x), - seed + 0x2000, - ); - - const newCommitmentsSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x3000); - const newNullifiersSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x4000); - const newContractsSubtreeSiblingPath = makeTuple(CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x5000); - - const sortedNewNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, fr, seed + 0x6000); - const sortednewNullifiersIndexes = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i, seed + 0x7000); - - const newPublicDataUpdateRequestsSiblingPaths = makeTuple( - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, - x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), - seed + 0x8000, - ); - - const newPublicDataReadsSiblingPaths = makeTuple( - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, - x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), - seed + 0x8000, - ); - - const blocksTreeRootMembershipWitnesses = makeTuple(KERNELS_PER_BASE_ROLLUP, x => - makeMembershipWitness(BLOCKS_TREE_HEIGHT, seed + x * 0x1000 + 0x9000), - ); - - const constants = makeConstantBaseRollupData(0x100); - - return BaseRollupInputs.from({ - kernelData, - lowNullifierMembershipWitness, - startNoteHashTreeSnapshot, - startNullifierTreeSnapshot, - startContractTreeSnapshot, - startPublicDataTreeRoot, - startBlocksTreeSnapshot, - sortedNewNullifiers, - sortednewNullifiersIndexes, - lowNullifierLeafPreimages, - newCommitmentsSubtreeSiblingPath, - newNullifiersSubtreeSiblingPath, - newContractsSubtreeSiblingPath, - newPublicDataUpdateRequestsSiblingPaths, - newPublicDataReadsSiblingPaths, - blocksTreeRootMembershipWitnesses, - constants, - }); -} +// /** +// * Makes arbitrary base rollup inputs. +// * @param seed - The seed to use for generating the base rollup inputs. +// * @returns A base rollup inputs. +// */ +// export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { +// const kernelData = makeTuple(KERNELS_PER_BASE_ROLLUP, x => makePreviousKernelData(seed + (x + 1) * 0x100)); + +// const startNoteHashTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x100); +// const startNullifierTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x200); +// const startContractTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x300); +// const startPublicDataTreeRoot = fr(seed + 0x400); +// const startBlocksTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x500); + +// const lowNullifierLeafPreimages = makeTuple( +// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, +// x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), +// seed + 0x1000, +// ); + +// const lowNullifierMembershipWitness = makeTuple( +// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, +// x => makeMembershipWitness(NULLIFIER_TREE_HEIGHT, x), +// seed + 0x2000, +// ); + +// const newCommitmentsSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x3000); +// const newNullifiersSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x4000); +// const newContractsSubtreeSiblingPath = makeTuple(CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x5000); + +// const sortedNewNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, fr, seed + 0x6000); +// const sortednewNullifiersIndexes = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i, seed + 0x7000); + +// const newPublicDataUpdateRequestsSiblingPaths = makeTuple( +// MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, +// x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), +// seed + 0x8000, +// ); + +// const newPublicDataReadsSiblingPaths = makeTuple( +// MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, +// x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), +// seed + 0x8000, +// ); + +// const blocksTreeRootMembershipWitnesses = makeTuple(KERNELS_PER_BASE_ROLLUP, x => +// makeMembershipWitness(BLOCKS_TREE_HEIGHT, seed + x * 0x1000 + 0x9000), +// ); + +// const constants = makeConstantBaseRollupData(0x100); + +// return BaseRollupInputs.from({ +// kernelData, +// lowNullifierMembershipWitness, +// startNoteHashTreeSnapshot, +// startNullifierTreeSnapshot, +// startContractTreeSnapshot, +// startPublicDataTreeRoot, +// startBlocksTreeSnapshot, +// sortedNewNullifiers, +// sortednewNullifiersIndexes, +// lowNullifierLeafPreimages, +// newCommitmentsSubtreeSiblingPath, +// newNullifiersSubtreeSiblingPath, +// newContractsSubtreeSiblingPath, +// newPublicDataUpdateRequestsSiblingPaths, +// publicDataReadsSiblingPaths: newPublicDataReadsSiblingPaths, +// blocksTreeRootMembershipWitnesses, +// constants, +// }); +// } /** * TODO: Since the max value check is currently disabled this function is pointless. Should it be removed? diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 2b13e560420f..6ad7f6d7d86b 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -133,7 +133,7 @@ describe('e2e_card_game', () => { ); }, 30_000); - it('should be able to join games', async () => { + it.only('should be able to join games', async () => { await contract.methods .join_game(GAME_ID, [cardToField(firstPlayerCollection[0]), cardToField(firstPlayerCollection[2])]) .send() diff --git a/yarn-project/foundation/src/trees/index.ts b/yarn-project/foundation/src/trees/index.ts index 030a59f2570b..8f09afbe997e 100644 --- a/yarn-project/foundation/src/trees/index.ts +++ b/yarn-project/foundation/src/trees/index.ts @@ -14,6 +14,12 @@ export interface IndexedTreeLeaf { * Returns true if the leaf is empty. */ isEmpty(): boolean; + + /** + * Updates the leaf with the data of another leaf. + * @param another - The leaf to update to. + */ + updateTo(another: IndexedTreeLeaf): void; } /** diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index a935bcef75ef..5d89f19d21e2 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -502,6 +502,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { }; } + const isUpdate = indexOfPrevious.alreadyPresent; + // get the low leaf (existence checked in getting index) const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(indexOfPrevious.index, true))!; const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); @@ -515,23 +517,36 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { // Update the running paths lowLeavesWitnesses[i] = witness; - const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf( - newLeaf, - lowLeafPreimage.getNextKey(), - lowLeafPreimage.getNextIndex(), - ); + if (isUpdate) { + const lowLeaf = lowLeafPreimage.asLeaf(); + newLeaf.updateTo(newLeaf); + + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + lowLeaf, + lowLeafPreimage.getKey(), + lowLeafPreimage.getNextIndex(), + ); + + await this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); - pendingInsertionSubtree[originalIndex] = currentPendingPreimageLeaf; + pendingInsertionSubtree[originalIndex] = this.leafPreimageFactory.empty(); + } else { + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + lowLeafPreimage.asLeaf(), + newLeaf.getKey(), + startInsertionIndex + BigInt(originalIndex), + ); - const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( - lowLeafPreimage.asLeaf(), - newLeaf.getKey(), - startInsertionIndex + BigInt(originalIndex), - ); + await this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); - const lowLeafIndex = indexOfPrevious.index; - this.cachedLeafPreimages[lowLeafIndex.toString()] = newLowLeafPreimage; - await this.updateLeaf(newLowLeafPreimage, lowLeafIndex); + const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf( + newLeaf, + lowLeafPreimage.getNextKey(), + lowLeafPreimage.getNextIndex(), + ); + + pendingInsertionSubtree[originalIndex] = currentPendingPreimageLeaf; + } } const newSubtreeSiblingPath = await this.getSubtreeSiblingPath( diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr index 8bd199879b56..b24405da0c23 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr @@ -149,7 +149,7 @@ fn propagate_valid_public_data_update_requests(public_call: PublicCallData, circ let update_request = update_requests[i]; if (!update_request.is_empty()) { let public_data_update_request = PublicDataUpdateRequest { - leaf_index: compute_public_data_tree_index(contract_address, update_request.storage_slot), + leaf_slot: compute_public_data_tree_index(contract_address, update_request.storage_slot), old_value: compute_public_data_tree_value(update_request.old_value), new_value: compute_public_data_tree_value(update_request.new_value), }; @@ -170,7 +170,7 @@ fn propagate_valid_public_data_reads(public_call: PublicCallData, circuit_output let read_request: StorageRead = read_requests[i]; if !read_request.is_empty() { let public_data_read = PublicDataRead { - leaf_index: compute_public_data_tree_index(contract_address, read_request.storage_slot), + leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_slot), value: compute_public_data_tree_value(read_request.current_value), }; public_data_reads.push(public_data_read); diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/utils.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/utils.nr index e6d8603cb77c..a2d377a03e43 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/utils.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/utils.nr @@ -22,7 +22,7 @@ pub fn compute_public_data_reads(contract_address: Address, read_requests: [S let read_request = read_requests[i]; if !read_request.is_empty() { public_data_reads[i] = PublicDataRead { - leaf_index: compute_public_data_tree_index(contract_address, read_request.storage_slot), + leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_slot), value: compute_public_data_tree_value(read_request.current_value), }; } @@ -51,7 +51,7 @@ pub fn compute_public_data_update_requests( let update_request = update_requests[i]; if !update_request.is_empty() { public_data_update_requests[i] = PublicDataUpdateRequest { - leaf_index: compute_public_data_tree_index(contract_address, update_request.storage_slot), + leaf_slot: compute_public_data_tree_index(contract_address, update_request.storage_slot), old_value: compute_public_data_tree_value(update_request.old_value), new_value: compute_public_data_tree_value(update_request.new_value), }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis.nr index 610e10e5e454..e8a3e182b10c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis.nr @@ -1,4 +1,5 @@ mod nullifier_leaf_preimage; +mod public_data_tree_leaf; mod append_only_tree_snapshot; mod global_variables; mod constant_rollup_data; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr new file mode 100644 index 000000000000..e3a6e7e3658c --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr @@ -0,0 +1,49 @@ +struct PublicDataTreeLeafPreimage { + slot : Field, + value: Field, + next_slot :Field, + next_index : u32, +} + +impl PublicDataTreeLeafPreimage { + pub fn default() -> Self { + Self { + slot: 0, + value: 0, + next_slot: 0, + next_index: 0, + } + } + + pub fn is_empty(self) -> bool { + (self.slot == 0) & (self.value == 0) & (self.next_slot == 0) & (self.next_index == 0) + } + + pub fn hash(self) -> Field { + if self.is_empty() { + 0 + } else { + dep::std::hash::pedersen_hash([self.slot, self.value, (self.next_index as Field), self.next_slot]) + } + } +} + +struct PublicDataTreeLeaf { + slot: Field, + value: Field, +} + + +impl PublicDataTreeLeaf { + pub fn default() -> Self { + Self { + slot: 0, + value: 0, + } + } + + pub fn is_empty(self) -> bool { + (self.slot == 0) & (self.value == 0) + } + +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index a453771ce150..89edde91fa5d 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -1,4 +1,5 @@ use crate::abis::nullifier_leaf_preimage::NullifierLeafPreimage; +use crate::abis::public_data_tree_leaf::{PublicDataTreeLeaf, PublicDataTreeLeafPreimage}; use crate::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot; use crate::abis::constant_rollup_data::ConstantRollupData; use crate::abis::base_or_merge_rollup_public_inputs::{BaseOrMergeRollupPublicInputs, BASE_ROLLUP_TYPE}; @@ -30,9 +31,10 @@ use dep::aztec::constants_gen::{ NUM_UNENCRYPTED_LOGS_HASHES_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, }; use dep::types::abis::previous_kernel_data::PreviousKernelData; -use dep::types::abis::membership_witness::{NullifierMembershipWitness, MembershipWitness}; +use dep::types::abis::membership_witness::{NullifierMembershipWitness, PublicDataMembershipWitness, MembershipWitness}; use dep::types::abis::membership_witness::BlocksTreeRootMembershipWitness; struct BaseRollupInputs { @@ -52,10 +54,17 @@ struct BaseRollupInputs { // Note: the insertion leaf index can be derived from the above snapshots' `next_available_leaf_index` values. new_commitments_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], new_nullifiers_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + public_data_writes_subtree_sibling_paths: [[Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]; KERNELS_PER_BASE_ROLLUP], new_contracts_subtree_sibling_path: [Field; CONTRACT_SUBTREE_SIBLING_PATH_LENGTH], - new_public_data_update_requests_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP], - new_public_data_reads_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP], + + sorted_public_data_writes: [[PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + sorted_public_data_writes_indexes: [[u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + low_public_data_writes_preimages: [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + low_public_data_writes_witnesses: [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + public_data_reads_preimages: [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + public_data_reads_witnesses: [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP], + blocks_tree_root_membership_witnesses: [BlocksTreeRootMembershipWitness; KERNELS_PER_BASE_ROLLUP], constants: ConstantRollupData, @@ -241,47 +250,47 @@ impl BaseRollupInputs { } fn validate_and_process_public_state(self) -> Field { - // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // Blocks all interesting usecases that read and write to the same public state in the same tx. - // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - - - // Process public data reads and public data update requests for left input - // validate_public_data_reads( - // self.start_public_data_tree_root, - // self.kernel_data[0].public_inputs.end.public_data_reads, - // 0, - // self.new_public_data_reads_sibling_paths); - - let mid_public_data_tree_root = insert_public_data_update_requests( - self.start_public_data_tree_root, - self.kernel_data[0].public_inputs.end.public_data_update_requests, - 0, - self.new_public_data_update_requests_sibling_paths - ); - - - // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // Blocks all interesting usecases that read and write to the same public state in the same tx. - // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - - - // Process public data reads and public data update requests for right input using the resulting tree root from the - // left one - // validate_public_data_reads( - // mid_public_data_tree_root, - // baseRollupInputs.kernel_data[1].public_inputs.end.public_data_reads, - // MAX_PUBLIC_DATA_READS_PER_TX, - // baseRollupInputs.new_public_data_reads_sibling_paths); - - let end_public_data_tree_root = insert_public_data_update_requests( - mid_public_data_tree_root, - self.kernel_data[1].public_inputs.end.public_data_update_requests, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - self.new_public_data_update_requests_sibling_paths - ); - - end_public_data_tree_root + // // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. + // // Blocks all interesting usecases that read and write to the same public state in the same tx. + // // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 + + + // // Process public data reads and public data update requests for left input + // // validate_public_data_reads( + // // self.start_public_data_tree_root, + // // self.kernel_data[0].public_inputs.end.public_data_reads, + // // 0, + // // self.new_public_data_reads_sibling_paths); + + // let mid_public_data_tree_root = insert_public_data_update_requests( + // self.start_public_data_tree_root, + // self.kernel_data[0].public_inputs.end.public_data_update_requests, + // 0, + // self.new_public_data_update_requests_sibling_paths + // ); + + + // // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. + // // Blocks all interesting usecases that read and write to the same public state in the same tx. + // // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 + + + // // Process public data reads and public data update requests for right input using the resulting tree root from the + // // left one + // // validate_public_data_reads( + // // mid_public_data_tree_root, + // // baseRollupInputs.kernel_data[1].public_inputs.end.public_data_reads, + // // MAX_PUBLIC_DATA_READS_PER_TX, + // // baseRollupInputs.new_public_data_reads_sibling_paths); + + // let end_public_data_tree_root = insert_public_data_update_requests( + // mid_public_data_tree_root, + // self.kernel_data[1].public_inputs.end.public_data_update_requests, + // MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + // self.new_public_data_update_requests_sibling_paths + // ); + + self.start_public_data_tree_root } // Computes the calldata hash for a base rollup @@ -321,7 +330,7 @@ impl BaseRollupInputs { for j in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { calldata_hash_inputs[offset + i * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + j * 2] = - public_data_update_requests[j].leaf_index; + public_data_update_requests[j].leaf_slot; calldata_hash_inputs[offset + i * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + j * 2 + 1] = public_data_update_requests[j].new_value; } @@ -412,47 +421,47 @@ fn verify_kernel_proof(proof: Proof) -> bool { true } -fn insert_public_data_update_requests( - tree_root: Field, - public_data_update_requests: [PublicDataUpdateRequest;MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - witnesses_offset: Field, - witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] -) -> Field { - let mut root = tree_root; - - for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - let state_write = public_data_update_requests[i]; - let witness = witnesses[i + witnesses_offset]; - - if (!state_write.is_empty()) { - components::assert_check_membership(state_write.old_value, state_write.leaf_index, witness, root); - root = components::root_from_sibling_path(state_write.new_value, state_write.leaf_index, witness); - } - } - - root -} - -fn validate_public_data_reads( - tree_root: Field, - public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], - witnesses_offset: Field, - witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_READS_PER_TX] -) { - for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { - let public_data_read = public_data_reads[i]; - let witness = witnesses[i + witnesses_offset]; - - if (!public_data_read.is_empty()) { - components::assert_check_membership( - public_data_read.value, - public_data_read.leaf_index, - witness, - tree_root - ); - } - } -} +// fn insert_public_data_update_requests( +// tree_root: Field, +// public_data_update_requests: [PublicDataUpdateRequest;MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], +// witnesses_offset: Field, +// witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] +// ) -> Field { +// let mut root = tree_root; + +// for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { +// let state_write = public_data_update_requests[i]; +// let witness = witnesses[i + witnesses_offset]; + +// if (!state_write.is_empty()) { +// components::assert_check_membership(state_write.old_value, state_write.leaf_index, witness, root); +// root = components::root_from_sibling_path(state_write.new_value, state_write.leaf_index, witness); +// } +// } + +// root +// } + +// fn validate_public_data_reads( +// tree_root: Field, +// public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], +// witnesses_offset: Field, +// witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_READS_PER_TX] +// ) { +// for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { +// let public_data_read = public_data_reads[i]; +// let witness = witnesses[i + witnesses_offset]; + +// if (!public_data_read.is_empty()) { +// components::assert_check_membership( +// public_data_read.value, +// public_data_read.leaf_slot, +// witness, +// tree_root +// ); +// } +// } +// } global NUM_CONTRACT_LEAVES = 2; #[test] @@ -520,719 +529,719 @@ fn test_u256_greater_than() { assert(full_field_greater_than(0 - 1, 0)); } -mod tests { - use crate::{ - base::base_rollup_inputs::{ - CALL_DATA_HASH_FULL_FIELDS, - CALL_DATA_HASH_LOG_FIELDS, - NOTE_HASH_SUBTREE_WIDTH, - NUM_CONTRACT_LEAVES, - BaseRollupInputs, - full_field_less_than, - }, - merkle_tree::{calculate_subtree, calculate_empty_tree_root}, - abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, - abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - abis::nullifier_leaf_preimage::NullifierLeafPreimage, - abis::constant_rollup_data::ConstantRollupData, - tests::merkle_tree_utils::{NonEmptyMerkleTree, compute_zero_hashes}, - components, - }; - use dep::aztec::constants_gen::{ - CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, - CONTRACT_TREE_HEIGHT, - CONTRACT_SUBTREE_HEIGHT, - BLOCKS_TREE_HEIGHT, - KERNELS_PER_BASE_ROLLUP, - MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, - NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, - NOTE_HASH_TREE_HEIGHT, - NOTE_HASH_SUBTREE_HEIGHT, - NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, - NULLIFIER_TREE_HEIGHT, - NULLIFIER_SUBTREE_HEIGHT, - PUBLIC_DATA_TREE_HEIGHT, - }; - use dep::types::{ - abis::membership_witness::BlocksTreeRootMembershipWitness, - abis::membership_witness::NullifierMembershipWitness, - abis::new_contract_data::NewContractData, - abis::public_data_read::PublicDataRead, - abis::public_data_update_request::PublicDataUpdateRequest, - abis::previous_kernel_data::PreviousKernelData, - tests::previous_kernel_data_builder::PreviousKernelDataBuilder, - address::{Address, EthAddress}, - utils::bounded_vec::BoundedVec, - utils::uint256::U256, - }; - use dep::std::option::Option; - - struct NullifierInsertion { - existing_index: u64, - value: Field, - } - - - struct SortedNullifierTuple { - value: Field, - original_index: u32, - } - - global MAX_NEW_NULLIFIERS_PER_TEST = 4; - - struct BaseRollupInputsBuilder { - kernel_data: [PreviousKernelDataBuilder; KERNELS_PER_BASE_ROLLUP], - pre_existing_notes: [Field; NOTE_HASH_SUBTREE_WIDTH], - pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], - pre_existing_contracts: [Field; NUM_CONTRACT_LEAVES], - pre_existing_public_data: [Field; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP], - pre_existing_blocks: [Field; KERNELS_PER_BASE_ROLLUP], - public_data_reads: BoundedVec, - public_data_writes: BoundedVec<(u64, Field), 2>, - new_nullifiers: BoundedVec, - constants: ConstantRollupData, - } - - fn test_compute_empty_root(size: [Field; N]) -> Field { - compute_zero_hashes(size)[N - 1] - } - - impl BaseRollupInputsBuilder { - fn new() -> Self { - let mut inputs: BaseRollupInputsBuilder = dep::std::unsafe::zeroed(); - inputs.constants.global_variables.chain_id = 1; - inputs.constants.global_variables.version = 0; - - inputs.kernel_data = inputs.kernel_data.map(|_| { - let mut builder = PreviousKernelDataBuilder::new(); - let _nullifier = builder.end.new_nullifiers.pop(); - builder.is_public() - }); - - inputs.pre_existing_blocks = inputs.kernel_data.map(|builder: PreviousKernelDataBuilder|{ - builder.block_header.block.hash() - }); - - inputs - } - - fn extract_subtree_sibling_path(path: [Field; FULL_HEIGHT], mut sibling_path: [Field; SIBLING_PATH_LENGTH]) -> [Field; SIBLING_PATH_LENGTH] { - let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; - for i in subtree_height..FULL_HEIGHT { - sibling_path[i - subtree_height] = path[i]; - } - sibling_path - } - - fn update_nullifier_tree_with_new_leaves( - mut self, - nullifier_tree: &mut NonEmptyMerkleTree, - kernel_data: &mut [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], - start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot - ) -> ( - [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], - [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], - [Field; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], - [u32; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], - ) { - let mut low_nullifier_leaf_preimages: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - let mut low_nullifier_membership_witness: [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - - let mut sorted_new_nullifier_tuples = [SortedNullifierTuple { - value: 0, - original_index: 0, - }; MAX_NEW_NULLIFIERS_PER_TEST]; - - - for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { - sorted_new_nullifier_tuples[i] = SortedNullifierTuple { - value: self.new_nullifiers.get_unchecked(i).value, - original_index: i as u32, - }; - } - sorted_new_nullifier_tuples = sorted_new_nullifier_tuples.sort_via(|a: SortedNullifierTuple, b: SortedNullifierTuple| {full_field_less_than(b.value, a.value)}); - - let mut sorted_new_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; - let mut sorted_new_nullifiers_indexes = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; - - for i in 0..MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP { - if (i as u32) < (MAX_NEW_NULLIFIERS_PER_TEST as u32) { - sorted_new_nullifiers[i] = sorted_new_nullifier_tuples[i].value; - sorted_new_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; - } else { - sorted_new_nullifiers[i] = 0; - sorted_new_nullifiers_indexes[i] = i as u32; - } - } - - let mut pre_existing_nullifiers = self.pre_existing_nullifiers; - - for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { - if (i as u64) < (self.new_nullifiers.len() as u64) { - let sorted_tuple = sorted_new_nullifier_tuples[i]; - let new_nullifier = sorted_tuple.value; - let original_index = sorted_tuple.original_index; - - let low_index = self.new_nullifiers.get_unchecked(original_index as Field).existing_index; - - kernel_data[0].public_inputs.end.new_nullifiers[original_index] = new_nullifier; - - let mut low_preimage = pre_existing_nullifiers[low_index]; - low_nullifier_leaf_preimages[i] = low_preimage; - low_nullifier_membership_witness[i] = NullifierMembershipWitness { - leaf_index: low_index as Field, - sibling_path: nullifier_tree.get_sibling_path(low_index as Field) - }; - - low_preimage.next_value = new_nullifier; - low_preimage.next_index = start_nullifier_tree_snapshot.next_available_leaf_index + original_index; - pre_existing_nullifiers[low_index] = low_preimage; - - nullifier_tree.update_leaf(low_index, low_preimage.hash()); - } - } - - (low_nullifier_leaf_preimages, low_nullifier_membership_witness, sorted_new_nullifiers, sorted_new_nullifiers_indexes) - } - - fn build_inputs(mut self) -> BaseRollupInputs { - let mut kernel_data = self.kernel_data.map(|builder: PreviousKernelDataBuilder|{ - builder.finish() - }); - - let start_note_hash_tree = NonEmptyMerkleTree::new(self.pre_existing_notes, [0; NOTE_HASH_TREE_HEIGHT], [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], [0; NOTE_HASH_SUBTREE_HEIGHT]); - let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_note_hash_tree.get_root(), - next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, - }; - let new_commitments_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH]); - - let mut start_nullifier_tree = NonEmptyMerkleTree::new( - self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], - [0; NULLIFIER_SUBTREE_HEIGHT] - ); - - let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_nullifier_tree.get_root(), - next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, - }; - - let start_contract_tree = NonEmptyMerkleTree::new(self.pre_existing_contracts, [0; CONTRACT_TREE_HEIGHT], [0; CONTRACT_TREE_HEIGHT - 1], [0; 1]); - let start_contract_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_contract_tree.get_root(), - next_available_leaf_index: start_contract_tree.get_next_available_index() as u32, - }; - let new_contracts_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_contract_tree.get_sibling_path(self.pre_existing_contracts.len()), [0; CONTRACT_SUBTREE_SIBLING_PATH_LENGTH]); - - let mut start_public_data_tree = NonEmptyMerkleTree::new(self.pre_existing_public_data, [0; PUBLIC_DATA_TREE_HEIGHT], [0; PUBLIC_DATA_TREE_HEIGHT - 5], [0; 5]); - let start_public_data_tree_root = start_public_data_tree.get_root(); - - let start_blocks_tree = NonEmptyMerkleTree::new(self.pre_existing_blocks, [0; BLOCKS_TREE_HEIGHT], [0; BLOCKS_TREE_HEIGHT - 1], [0; 1]); - let start_blocks_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_blocks_tree.get_root(), - next_available_leaf_index: start_blocks_tree.get_next_available_index() as u32, - }; - - self.constants.start_blocks_tree_snapshot = start_blocks_tree_snapshot; - - let mut new_public_data_reads_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - - for i in 0..self.public_data_reads.max_len() { - if (i as u64) < (self.public_data_reads.len() as u64) { - let index = self.public_data_reads.get_unchecked(i); - let value = self.pre_existing_public_data[index]; - kernel_data[0].public_inputs.end.public_data_reads[i] = PublicDataRead { - leaf_index: index as Field, - value: value, - }; - new_public_data_reads_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); - } - } - - let mut new_public_data_update_requests_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - - for i in 0..self.public_data_writes.max_len() { - if (i as u64) < (self.public_data_writes.len() as u64) { - let write = self.public_data_writes.get_unchecked(i); - let index = write.0; - let new_value = write.1; - let old_value = self.pre_existing_public_data[index]; - kernel_data[0].public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { - leaf_index : index as Field, - old_value, - new_value, - }; - new_public_data_update_requests_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); - start_public_data_tree.update_leaf(index, new_value); - } - } - - let ( - low_nullifier_leaf_preimages, - low_nullifier_membership_witness, - sorted_new_nullifiers, - sorted_new_nullifiers_indexes - ) = self.update_nullifier_tree_with_new_leaves(&mut start_nullifier_tree, &mut kernel_data, start_nullifier_tree_snapshot); +// mod tests { +// use crate::{ +// base::base_rollup_inputs::{ +// CALL_DATA_HASH_FULL_FIELDS, +// CALL_DATA_HASH_LOG_FIELDS, +// NOTE_HASH_SUBTREE_WIDTH, +// NUM_CONTRACT_LEAVES, +// BaseRollupInputs, +// full_field_less_than, +// }, +// merkle_tree::{calculate_subtree, calculate_empty_tree_root}, +// abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, +// abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, +// abis::nullifier_leaf_preimage::NullifierLeafPreimage, +// abis::constant_rollup_data::ConstantRollupData, +// tests::merkle_tree_utils::{NonEmptyMerkleTree, compute_zero_hashes}, +// components, +// }; +// use dep::aztec::constants_gen::{ +// CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, +// CONTRACT_TREE_HEIGHT, +// CONTRACT_SUBTREE_HEIGHT, +// BLOCKS_TREE_HEIGHT, +// KERNELS_PER_BASE_ROLLUP, +// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, +// MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, +// MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, +// NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, +// NOTE_HASH_TREE_HEIGHT, +// NOTE_HASH_SUBTREE_HEIGHT, +// NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, +// NULLIFIER_TREE_HEIGHT, +// NULLIFIER_SUBTREE_HEIGHT, +// PUBLIC_DATA_TREE_HEIGHT, +// }; +// use dep::types::{ +// abis::membership_witness::BlocksTreeRootMembershipWitness, +// abis::membership_witness::NullifierMembershipWitness, +// abis::new_contract_data::NewContractData, +// abis::public_data_read::PublicDataRead, +// abis::public_data_update_request::PublicDataUpdateRequest, +// abis::previous_kernel_data::PreviousKernelData, +// tests::previous_kernel_data_builder::PreviousKernelDataBuilder, +// address::{Address, EthAddress}, +// utils::bounded_vec::BoundedVec, +// utils::uint256::U256, +// }; +// use dep::std::option::Option; + +// struct NullifierInsertion { +// existing_index: u64, +// value: Field, +// } + + +// struct SortedNullifierTuple { +// value: Field, +// original_index: u32, +// } + +// global MAX_NEW_NULLIFIERS_PER_TEST = 4; + +// struct BaseRollupInputsBuilder { +// kernel_data: [PreviousKernelDataBuilder; KERNELS_PER_BASE_ROLLUP], +// pre_existing_notes: [Field; NOTE_HASH_SUBTREE_WIDTH], +// pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], +// pre_existing_contracts: [Field; NUM_CONTRACT_LEAVES], +// pre_existing_public_data: [Field; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP], +// pre_existing_blocks: [Field; KERNELS_PER_BASE_ROLLUP], +// public_data_reads: BoundedVec, +// public_data_writes: BoundedVec<(u64, Field), 2>, +// new_nullifiers: BoundedVec, +// constants: ConstantRollupData, +// } + +// fn test_compute_empty_root(size: [Field; N]) -> Field { +// compute_zero_hashes(size)[N - 1] +// } + +// impl BaseRollupInputsBuilder { +// fn new() -> Self { +// let mut inputs: BaseRollupInputsBuilder = dep::std::unsafe::zeroed(); +// inputs.constants.global_variables.chain_id = 1; +// inputs.constants.global_variables.version = 0; + +// inputs.kernel_data = inputs.kernel_data.map(|_| { +// let mut builder = PreviousKernelDataBuilder::new(); +// let _nullifier = builder.end.new_nullifiers.pop(); +// builder.is_public() +// }); + +// inputs.pre_existing_blocks = inputs.kernel_data.map(|builder: PreviousKernelDataBuilder|{ +// builder.block_header.block.hash() +// }); + +// inputs +// } + +// fn extract_subtree_sibling_path(path: [Field; FULL_HEIGHT], mut sibling_path: [Field; SIBLING_PATH_LENGTH]) -> [Field; SIBLING_PATH_LENGTH] { +// let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; +// for i in subtree_height..FULL_HEIGHT { +// sibling_path[i - subtree_height] = path[i]; +// } +// sibling_path +// } + +// fn update_nullifier_tree_with_new_leaves( +// mut self, +// nullifier_tree: &mut NonEmptyMerkleTree, +// kernel_data: &mut [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], +// start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot +// ) -> ( +// [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], +// [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], +// [Field; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], +// [u32; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], +// ) { +// let mut low_nullifier_leaf_preimages: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); +// let mut low_nullifier_membership_witness: [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + +// let mut sorted_new_nullifier_tuples = [SortedNullifierTuple { +// value: 0, +// original_index: 0, +// }; MAX_NEW_NULLIFIERS_PER_TEST]; + + +// for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { +// sorted_new_nullifier_tuples[i] = SortedNullifierTuple { +// value: self.new_nullifiers.get_unchecked(i).value, +// original_index: i as u32, +// }; +// } +// sorted_new_nullifier_tuples = sorted_new_nullifier_tuples.sort_via(|a: SortedNullifierTuple, b: SortedNullifierTuple| {full_field_less_than(b.value, a.value)}); + +// let mut sorted_new_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; +// let mut sorted_new_nullifiers_indexes = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; + +// for i in 0..MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP { +// if (i as u32) < (MAX_NEW_NULLIFIERS_PER_TEST as u32) { +// sorted_new_nullifiers[i] = sorted_new_nullifier_tuples[i].value; +// sorted_new_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; +// } else { +// sorted_new_nullifiers[i] = 0; +// sorted_new_nullifiers_indexes[i] = i as u32; +// } +// } + +// let mut pre_existing_nullifiers = self.pre_existing_nullifiers; + +// for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { +// if (i as u64) < (self.new_nullifiers.len() as u64) { +// let sorted_tuple = sorted_new_nullifier_tuples[i]; +// let new_nullifier = sorted_tuple.value; +// let original_index = sorted_tuple.original_index; + +// let low_index = self.new_nullifiers.get_unchecked(original_index as Field).existing_index; + +// kernel_data[0].public_inputs.end.new_nullifiers[original_index] = new_nullifier; + +// let mut low_preimage = pre_existing_nullifiers[low_index]; +// low_nullifier_leaf_preimages[i] = low_preimage; +// low_nullifier_membership_witness[i] = NullifierMembershipWitness { +// leaf_index: low_index as Field, +// sibling_path: nullifier_tree.get_sibling_path(low_index as Field) +// }; + +// low_preimage.next_value = new_nullifier; +// low_preimage.next_index = start_nullifier_tree_snapshot.next_available_leaf_index + original_index; +// pre_existing_nullifiers[low_index] = low_preimage; + +// nullifier_tree.update_leaf(low_index, low_preimage.hash()); +// } +// } + +// (low_nullifier_leaf_preimages, low_nullifier_membership_witness, sorted_new_nullifiers, sorted_new_nullifiers_indexes) +// } + +// fn build_inputs(mut self) -> BaseRollupInputs { +// let mut kernel_data = self.kernel_data.map(|builder: PreviousKernelDataBuilder|{ +// builder.finish() +// }); + +// let start_note_hash_tree = NonEmptyMerkleTree::new(self.pre_existing_notes, [0; NOTE_HASH_TREE_HEIGHT], [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], [0; NOTE_HASH_SUBTREE_HEIGHT]); +// let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { +// root: start_note_hash_tree.get_root(), +// next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, +// }; +// let new_commitments_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH]); + +// let mut start_nullifier_tree = NonEmptyMerkleTree::new( +// self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), +// [0; NULLIFIER_TREE_HEIGHT], +// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], +// [0; NULLIFIER_SUBTREE_HEIGHT] +// ); + +// let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { +// root: start_nullifier_tree.get_root(), +// next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, +// }; + +// let start_contract_tree = NonEmptyMerkleTree::new(self.pre_existing_contracts, [0; CONTRACT_TREE_HEIGHT], [0; CONTRACT_TREE_HEIGHT - 1], [0; 1]); +// let start_contract_tree_snapshot = AppendOnlyTreeSnapshot { +// root: start_contract_tree.get_root(), +// next_available_leaf_index: start_contract_tree.get_next_available_index() as u32, +// }; +// let new_contracts_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_contract_tree.get_sibling_path(self.pre_existing_contracts.len()), [0; CONTRACT_SUBTREE_SIBLING_PATH_LENGTH]); + +// let mut start_public_data_tree = NonEmptyMerkleTree::new(self.pre_existing_public_data, [0; PUBLIC_DATA_TREE_HEIGHT], [0; PUBLIC_DATA_TREE_HEIGHT - 5], [0; 5]); +// let start_public_data_tree_root = start_public_data_tree.get_root(); + +// let start_blocks_tree = NonEmptyMerkleTree::new(self.pre_existing_blocks, [0; BLOCKS_TREE_HEIGHT], [0; BLOCKS_TREE_HEIGHT - 1], [0; 1]); +// let start_blocks_tree_snapshot = AppendOnlyTreeSnapshot { +// root: start_blocks_tree.get_root(), +// next_available_leaf_index: start_blocks_tree.get_next_available_index() as u32, +// }; + +// self.constants.start_blocks_tree_snapshot = start_blocks_tree_snapshot; + +// let mut new_public_data_reads_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + +// for i in 0..self.public_data_reads.max_len() { +// if (i as u64) < (self.public_data_reads.len() as u64) { +// let index = self.public_data_reads.get_unchecked(i); +// let value = self.pre_existing_public_data[index]; +// kernel_data[0].public_inputs.end.public_data_reads[i] = PublicDataRead { +// leaf_index: index as Field, +// value: value, +// }; +// new_public_data_reads_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); +// } +// } + +// let mut new_public_data_update_requests_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + +// for i in 0..self.public_data_writes.max_len() { +// if (i as u64) < (self.public_data_writes.len() as u64) { +// let write = self.public_data_writes.get_unchecked(i); +// let index = write.0; +// let new_value = write.1; +// let old_value = self.pre_existing_public_data[index]; +// kernel_data[0].public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { +// leaf_index : index as Field, +// old_value, +// new_value, +// }; +// new_public_data_update_requests_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); +// start_public_data_tree.update_leaf(index, new_value); +// } +// } + +// let ( +// low_nullifier_leaf_preimages, +// low_nullifier_membership_witness, +// sorted_new_nullifiers, +// sorted_new_nullifiers_indexes +// ) = self.update_nullifier_tree_with_new_leaves(&mut start_nullifier_tree, &mut kernel_data, start_nullifier_tree_snapshot); - let new_nullifiers_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH]); - - BaseRollupInputs { - kernel_data: kernel_data, - start_note_hash_tree_snapshot, - start_nullifier_tree_snapshot, - start_contract_tree_snapshot, - start_public_data_tree_root, - start_blocks_tree_snapshot, - - sorted_new_nullifiers, - sorted_new_nullifiers_indexes, - - low_nullifier_leaf_preimages, - low_nullifier_membership_witness, - - new_commitments_subtree_sibling_path, - new_nullifiers_subtree_sibling_path, - new_contracts_subtree_sibling_path, - new_public_data_update_requests_sibling_paths, - new_public_data_reads_sibling_paths, - - blocks_tree_root_membership_witnesses: [ - BlocksTreeRootMembershipWitness { - leaf_index: 0, - sibling_path: start_blocks_tree.get_sibling_path(0) - }, - BlocksTreeRootMembershipWitness { - leaf_index: 1, - sibling_path: start_blocks_tree.get_sibling_path(1) - }, - ], - - constants: self.constants, - } - } - - fn execute(self) -> BaseOrMergeRollupPublicInputs { - self.build_inputs().base_rollup_circuit() - } - - fn succeeds(self) { - let _ = self.execute(); - } - - fn fails(self) { - let _ = self.execute(); - } - } - - #[test] - unconstrained fn no_new_contract_leaves() { - let outputs = BaseRollupInputsBuilder::new().execute(); - let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 2 }; - let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 4 }; - assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); - assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); - } - - #[test] - unconstrained fn contract_leaf_inserted() { - let new_contract = NewContractData { - contract_address: Address::from_field(1), - portal_contract_address: EthAddress::from_field(2), - function_tree_root: 3 - }; - - let mut builder = BaseRollupInputsBuilder::new(); - - let mut new_contracts = builder.kernel_data[0].end.new_contracts; - new_contracts.push(new_contract); - builder.kernel_data[0].end.new_contracts = new_contracts; - - let mut expected_contracts_tree = NonEmptyMerkleTree::new( - [0; 4], - [0; CONTRACT_TREE_HEIGHT], - [0; CONTRACT_TREE_HEIGHT - 2], - [0; 2] - ); - - let outputs = builder.execute(); - - let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; - assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); - - expected_contracts_tree.update_leaf(2, new_contract.hash()); - let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; - assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); - } - - #[test] - unconstrained fn contract_leaf_inserted_in_non_empty_snapshot_tree() { - let new_contract = NewContractData { - contract_address: Address::from_field(1), - portal_contract_address: EthAddress::from_field(2), - function_tree_root: 3 - }; - - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_contracts = [1,2]; - - let mut new_contracts = builder.kernel_data[0].end.new_contracts; - new_contracts.push(new_contract); - builder.kernel_data[0].end.new_contracts = new_contracts; - - let mut expected_contracts_tree = NonEmptyMerkleTree::new( - [1, 2, 0, 0], - [0; CONTRACT_TREE_HEIGHT], - [0; CONTRACT_TREE_HEIGHT - 2], - [0; 2] - ); - - let outputs = builder.execute(); - - let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; - assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); - - expected_contracts_tree.update_leaf(2, new_contract.hash()); - let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; - assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); - } - - #[test] - unconstrained fn new_commitments_tree() { - let mut builder = BaseRollupInputsBuilder::new(); - - let new_commitments = [27, 28, 29, 30, 31, 32]; - let mut new_commitments_vec = builder.kernel_data[0].end.new_commitments; - - for i in 0..new_commitments.len() { - new_commitments_vec.push(new_commitments[i]); - } - - builder.kernel_data[0].end.new_commitments = new_commitments_vec; - - let mut expected_commitments_tree = NonEmptyMerkleTree::new( - [0; NOTE_HASH_SUBTREE_WIDTH * 2], - [0; NOTE_HASH_TREE_HEIGHT], - [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], - [0; NOTE_HASH_SUBTREE_HEIGHT + 1] - ); - - let outputs = builder.execute(); - - let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH }; - assert(outputs.start_note_hash_tree_snapshot.eq(expected_start_note_hash_tree_snapshot)); - - for i in 0..new_commitments.len() { - expected_commitments_tree.update_leaf( - (i as u64) + (NOTE_HASH_SUBTREE_WIDTH as u64), - new_commitments[i] - ); - } - - let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH * 2 }; - assert(outputs.end_note_hash_tree_snapshot.eq(expected_end_note_hash_tree_snapshot)); - } - - #[test] - unconstrained fn new_nullifier_tree_empty() { - /** - * DESCRIPTION - */ - - // This test checks for insertions of all 0 values - // In this special case we will not need to provide sibling paths to check insertion of the nullifier values - // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left - // empty to begin with. - - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 7, - next_index : 1, - }; - builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 0, - next_index : 0, - }; - - builder.succeeds(); - } - - #[test] - unconstrained fn nullifier_insertion_test() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 7, - next_index : 1, - }; - builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 0, - next_index : 0, - }; - - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 0, - value: 1, - }); - - let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; - tree_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 1, - next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - }; - tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; - tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = NullifierLeafPreimage { - leaf_value : 1, - next_value : 7, - next_index : 1, - }; - - let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], - [0; NULLIFIER_SUBTREE_HEIGHT + 1] - ); - - let output = builder.execute(); - - assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { - root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - })); - } - - #[test] - unconstrained fn new_nullifier_tree_all_larger() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 7, - next_index : 1, - }; - builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 0, - next_index : 0, - }; - - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - for i in 1..builder.new_nullifiers.max_len() { - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: (8 + i) as Field, - }); - } - - let output = builder.execute(); - - let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; - tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; - - tree_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 8, - next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - }; - - let last_index = builder.new_nullifiers.max_len() - 1; - for i in 0..last_index { - tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i] = NullifierLeafPreimage { - leaf_value : (8 + i) as Field, - next_value : (8 + i + 1) as Field, - next_index : (MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i) as u32 + 1, - }; - } - - tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP+last_index] = NullifierLeafPreimage { - leaf_value : (8 + last_index) as Field, - next_value : 0, - next_index : 0, - }; - - let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], - [0; NULLIFIER_SUBTREE_HEIGHT + 1] - ); - - assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { - root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - })); - } - - #[test(should_fail_with = "Invalid low leaf")] - unconstrained fn new_nullifier_tree_double_spend() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 7, - next_index : 1, - }; - builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 0, - next_index : 0, - }; - - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - - builder.fails(); - } - - #[test(should_fail_with = "Invalid low leaf")] - unconstrained fn new_nullifier_tree_double_spend_same_batch() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { - leaf_value : 0, - next_value : 7, - next_index : 1, - }; - builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { - leaf_value : 7, - next_value : 0, - next_index : 0, - }; - - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - - builder.fails(); - } - - #[test] - unconstrained fn empty_block_calldata_hash() { - let outputs = BaseRollupInputsBuilder::new().execute(); - - let hash_input_flattened = [0; CALL_DATA_HASH_FULL_FIELDS * 32 + CALL_DATA_HASH_LOG_FIELDS * 16]; - let sha_digest = dep::std::hash::sha256(hash_input_flattened); - let expected_calldata_hash = U256::from_bytes32(sha_digest).to_u128_limbs(); - for i in 0..NUM_FIELDS_PER_SHA256 { - assert_eq(outputs.calldata_hash[i], expected_calldata_hash[i]); - } - } - - #[test(should_fail_with = "membership check failed")] - unconstrained fn compute_membership_blocks_tree_negative() { - let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); - - inputs.blocks_tree_root_membership_witnesses[0].sibling_path[0] = 27; - - let _output = inputs.base_rollup_circuit(); - } - - #[test] - unconstrained fn constants_dont_change() { - let inputs = BaseRollupInputsBuilder::new().build_inputs(); - let outputs = inputs.base_rollup_circuit(); - - assert(inputs.constants.eq(outputs.constants)); - } - - #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] - unconstrained fn constants_dont_match_kernels_chain_id() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.chain_id = 3; - builder.fails(); - } - - #[test(should_fail_with = "kernel version does not match the rollup version")] - unconstrained fn constants_dont_match_kernels_version() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.version = 3; - builder.fails(); - } - - #[test] - unconstrained fn subtree_height_is_0() { - let outputs = BaseRollupInputsBuilder::new().execute(); - - assert_eq(outputs.rollup_subtree_height, 0); - } - - #[test] - unconstrained fn single_public_state_read() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_public_data[0] = 27; - builder.public_data_reads.push(0); - - builder.succeeds(); - } - - #[test] - unconstrained fn single_public_state_write() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_public_data[0] = 27; - builder.public_data_writes.push((0, 28)); - - let outputs = builder.execute(); - - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - [28, 0], - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - 1], - [0; 1] - ); - - assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); - } - - #[test] - unconstrained fn multiple_public_state_read_writes() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_public_data[0] = 27; - builder.pre_existing_public_data[1] = 28; - builder.pre_existing_public_data[2] = 29; - builder.pre_existing_public_data[3] = 30; - - builder.public_data_reads.push(0); - builder.public_data_writes.push((0, 60)); - builder.public_data_writes.push((2, 61)); - builder.public_data_reads.push(3); - - let outputs = builder.execute(); - - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - [60, 28, 61, 30], - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - 2], - [0; 2] - ); - - assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); - } -} \ No newline at end of file +// let new_nullifiers_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH]); + +// BaseRollupInputs { +// kernel_data: kernel_data, +// start_note_hash_tree_snapshot, +// start_nullifier_tree_snapshot, +// start_contract_tree_snapshot, +// start_public_data_tree_root, +// start_blocks_tree_snapshot, + +// sorted_new_nullifiers, +// sorted_new_nullifiers_indexes, + +// low_nullifier_leaf_preimages, +// low_nullifier_membership_witness, + +// new_commitments_subtree_sibling_path, +// new_nullifiers_subtree_sibling_path, +// new_contracts_subtree_sibling_path, +// new_public_data_update_requests_sibling_paths, +// new_public_data_reads_sibling_paths, + +// blocks_tree_root_membership_witnesses: [ +// BlocksTreeRootMembershipWitness { +// leaf_index: 0, +// sibling_path: start_blocks_tree.get_sibling_path(0) +// }, +// BlocksTreeRootMembershipWitness { +// leaf_index: 1, +// sibling_path: start_blocks_tree.get_sibling_path(1) +// }, +// ], + +// constants: self.constants, +// } +// } + +// fn execute(self) -> BaseOrMergeRollupPublicInputs { +// self.build_inputs().base_rollup_circuit() +// } + +// fn succeeds(self) { +// let _ = self.execute(); +// } + +// fn fails(self) { +// let _ = self.execute(); +// } +// } + +// #[test] +// unconstrained fn no_new_contract_leaves() { +// let outputs = BaseRollupInputsBuilder::new().execute(); +// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 2 }; +// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 4 }; +// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); +// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); +// } + +// #[test] +// unconstrained fn contract_leaf_inserted() { +// let new_contract = NewContractData { +// contract_address: Address::from_field(1), +// portal_contract_address: EthAddress::from_field(2), +// function_tree_root: 3 +// }; + +// let mut builder = BaseRollupInputsBuilder::new(); + +// let mut new_contracts = builder.kernel_data[0].end.new_contracts; +// new_contracts.push(new_contract); +// builder.kernel_data[0].end.new_contracts = new_contracts; + +// let mut expected_contracts_tree = NonEmptyMerkleTree::new( +// [0; 4], +// [0; CONTRACT_TREE_HEIGHT], +// [0; CONTRACT_TREE_HEIGHT - 2], +// [0; 2] +// ); + +// let outputs = builder.execute(); + +// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; +// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); + +// expected_contracts_tree.update_leaf(2, new_contract.hash()); +// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; +// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); +// } + +// #[test] +// unconstrained fn contract_leaf_inserted_in_non_empty_snapshot_tree() { +// let new_contract = NewContractData { +// contract_address: Address::from_field(1), +// portal_contract_address: EthAddress::from_field(2), +// function_tree_root: 3 +// }; + +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_contracts = [1,2]; + +// let mut new_contracts = builder.kernel_data[0].end.new_contracts; +// new_contracts.push(new_contract); +// builder.kernel_data[0].end.new_contracts = new_contracts; + +// let mut expected_contracts_tree = NonEmptyMerkleTree::new( +// [1, 2, 0, 0], +// [0; CONTRACT_TREE_HEIGHT], +// [0; CONTRACT_TREE_HEIGHT - 2], +// [0; 2] +// ); + +// let outputs = builder.execute(); + +// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; +// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); + +// expected_contracts_tree.update_leaf(2, new_contract.hash()); +// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; +// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); +// } + +// #[test] +// unconstrained fn new_commitments_tree() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// let new_commitments = [27, 28, 29, 30, 31, 32]; +// let mut new_commitments_vec = builder.kernel_data[0].end.new_commitments; + +// for i in 0..new_commitments.len() { +// new_commitments_vec.push(new_commitments[i]); +// } + +// builder.kernel_data[0].end.new_commitments = new_commitments_vec; + +// let mut expected_commitments_tree = NonEmptyMerkleTree::new( +// [0; NOTE_HASH_SUBTREE_WIDTH * 2], +// [0; NOTE_HASH_TREE_HEIGHT], +// [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], +// [0; NOTE_HASH_SUBTREE_HEIGHT + 1] +// ); + +// let outputs = builder.execute(); + +// let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH }; +// assert(outputs.start_note_hash_tree_snapshot.eq(expected_start_note_hash_tree_snapshot)); + +// for i in 0..new_commitments.len() { +// expected_commitments_tree.update_leaf( +// (i as u64) + (NOTE_HASH_SUBTREE_WIDTH as u64), +// new_commitments[i] +// ); +// } + +// let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH * 2 }; +// assert(outputs.end_note_hash_tree_snapshot.eq(expected_end_note_hash_tree_snapshot)); +// } + +// #[test] +// unconstrained fn new_nullifier_tree_empty() { +// /** +// * DESCRIPTION +// */ + +// // This test checks for insertions of all 0 values +// // In this special case we will not need to provide sibling paths to check insertion of the nullifier values +// // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left +// // empty to begin with. + +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 7, +// next_index : 1, +// }; +// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 0, +// next_index : 0, +// }; + +// builder.succeeds(); +// } + +// #[test] +// unconstrained fn nullifier_insertion_test() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 7, +// next_index : 1, +// }; +// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 0, +// next_index : 0, +// }; + +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 0, +// value: 1, +// }); + +// let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; +// tree_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 1, +// next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, +// }; +// tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; +// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = NullifierLeafPreimage { +// leaf_value : 1, +// next_value : 7, +// next_index : 1, +// }; + +// let mut end_nullifier_tree = NonEmptyMerkleTree::new( +// tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), +// [0; NULLIFIER_TREE_HEIGHT], +// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], +// [0; NULLIFIER_SUBTREE_HEIGHT + 1] +// ); + +// let output = builder.execute(); + +// assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { +// root: end_nullifier_tree.get_root(), +// next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, +// })); +// } + +// #[test] +// unconstrained fn new_nullifier_tree_all_larger() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 7, +// next_index : 1, +// }; +// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 0, +// next_index : 0, +// }; + +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: 8, +// }); +// for i in 1..builder.new_nullifiers.max_len() { +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: (8 + i) as Field, +// }); +// } + +// let output = builder.execute(); + +// let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; +// tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; + +// tree_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 8, +// next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, +// }; + +// let last_index = builder.new_nullifiers.max_len() - 1; +// for i in 0..last_index { +// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i] = NullifierLeafPreimage { +// leaf_value : (8 + i) as Field, +// next_value : (8 + i + 1) as Field, +// next_index : (MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i) as u32 + 1, +// }; +// } + +// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP+last_index] = NullifierLeafPreimage { +// leaf_value : (8 + last_index) as Field, +// next_value : 0, +// next_index : 0, +// }; + +// let mut end_nullifier_tree = NonEmptyMerkleTree::new( +// tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), +// [0; NULLIFIER_TREE_HEIGHT], +// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], +// [0; NULLIFIER_SUBTREE_HEIGHT + 1] +// ); + +// assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { +// root: end_nullifier_tree.get_root(), +// next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, +// })); +// } + +// #[test(should_fail_with = "Invalid low leaf")] +// unconstrained fn new_nullifier_tree_double_spend() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 7, +// next_index : 1, +// }; +// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 0, +// next_index : 0, +// }; + +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: 8, +// }); +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: 8, +// }); + +// builder.fails(); +// } + +// #[test(should_fail_with = "Invalid low leaf")] +// unconstrained fn new_nullifier_tree_double_spend_same_batch() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { +// leaf_value : 0, +// next_value : 7, +// next_index : 1, +// }; +// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { +// leaf_value : 7, +// next_value : 0, +// next_index : 0, +// }; + +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: 8, +// }); +// builder.new_nullifiers.push(NullifierInsertion { +// existing_index: 1, +// value: 8, +// }); + +// builder.fails(); +// } + +// #[test] +// unconstrained fn empty_block_calldata_hash() { +// let outputs = BaseRollupInputsBuilder::new().execute(); + +// let hash_input_flattened = [0; CALL_DATA_HASH_FULL_FIELDS * 32 + CALL_DATA_HASH_LOG_FIELDS * 16]; +// let sha_digest = dep::std::hash::sha256(hash_input_flattened); +// let expected_calldata_hash = U256::from_bytes32(sha_digest).to_u128_limbs(); +// for i in 0..NUM_FIELDS_PER_SHA256 { +// assert_eq(outputs.calldata_hash[i], expected_calldata_hash[i]); +// } +// } + +// #[test(should_fail_with = "membership check failed")] +// unconstrained fn compute_membership_blocks_tree_negative() { +// let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); + +// inputs.blocks_tree_root_membership_witnesses[0].sibling_path[0] = 27; + +// let _output = inputs.base_rollup_circuit(); +// } + +// #[test] +// unconstrained fn constants_dont_change() { +// let inputs = BaseRollupInputsBuilder::new().build_inputs(); +// let outputs = inputs.base_rollup_circuit(); + +// assert(inputs.constants.eq(outputs.constants)); +// } + +// #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] +// unconstrained fn constants_dont_match_kernels_chain_id() { +// let mut builder = BaseRollupInputsBuilder::new(); +// builder.constants.global_variables.chain_id = 3; +// builder.fails(); +// } + +// #[test(should_fail_with = "kernel version does not match the rollup version")] +// unconstrained fn constants_dont_match_kernels_version() { +// let mut builder = BaseRollupInputsBuilder::new(); +// builder.constants.global_variables.version = 3; +// builder.fails(); +// } + +// #[test] +// unconstrained fn subtree_height_is_0() { +// let outputs = BaseRollupInputsBuilder::new().execute(); + +// assert_eq(outputs.rollup_subtree_height, 0); +// } + +// #[test] +// unconstrained fn single_public_state_read() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_public_data[0] = 27; +// builder.public_data_reads.push(0); + +// builder.succeeds(); +// } + +// #[test] +// unconstrained fn single_public_state_write() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_public_data[0] = 27; +// builder.public_data_writes.push((0, 28)); + +// let outputs = builder.execute(); + +// let mut expected_public_data_tree = NonEmptyMerkleTree::new( +// [28, 0], +// [0; PUBLIC_DATA_TREE_HEIGHT], +// [0; PUBLIC_DATA_TREE_HEIGHT - 1], +// [0; 1] +// ); + +// assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); +// } + +// #[test] +// unconstrained fn multiple_public_state_read_writes() { +// let mut builder = BaseRollupInputsBuilder::new(); + +// builder.pre_existing_public_data[0] = 27; +// builder.pre_existing_public_data[1] = 28; +// builder.pre_existing_public_data[2] = 29; +// builder.pre_existing_public_data[3] = 30; + +// builder.public_data_reads.push(0); +// builder.public_data_writes.push((0, 60)); +// builder.public_data_writes.push((2, 61)); +// builder.public_data_reads.push(3); + +// let outputs = builder.execute(); + +// let mut expected_public_data_tree = NonEmptyMerkleTree::new( +// [60, 28, 61, 30], +// [0; PUBLIC_DATA_TREE_HEIGHT], +// [0; PUBLIC_DATA_TREE_HEIGHT - 2], +// [0; 2] +// ); + +// assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); +// } +// } \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/membership_witness.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/membership_witness.nr index 6b4c29dcb71b..0395c49f2199 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/membership_witness.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/membership_witness.nr @@ -6,6 +6,7 @@ use dep::aztec::constants_gen::{ NOTE_HASH_TREE_HEIGHT, ROLLUP_VK_TREE_HEIGHT, BLOCKS_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, }; struct MembershipWitness { @@ -37,6 +38,11 @@ struct NullifierMembershipWitness{ sibling_path: [Field; NULLIFIER_TREE_HEIGHT] } +struct PublicDataMembershipWitness{ + leaf_index: Field, + sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT] +} + struct BlocksTreeRootMembershipWitness{ leaf_index: Field, sibling_path: [Field; BLOCKS_TREE_HEIGHT] diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_read.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_read.nr index 94c8a02d087a..5b7be8511284 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_read.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_read.nr @@ -1,30 +1,30 @@ use dep::aztec::constants_gen; struct PublicDataRead { - leaf_index : Field, + leaf_slot : Field, value : Field, } impl PublicDataRead { fn hash(self) -> Field { dep::std::hash::pedersen_hash_with_separator([ - self.leaf_index, + self.leaf_slot, self.value, ], constants_gen::GENERATOR_INDEX__PUBLIC_DATA_READ) } pub fn empty() -> Self { Self { - leaf_index : 0, + leaf_slot : 0, value : 0, } } pub fn is_empty(self) -> bool { - (self.leaf_index == 0) & (self.value == 0) + (self.leaf_slot == 0) & (self.value == 0) } pub fn eq(self, public_data_read: PublicDataRead) -> bool { - (public_data_read.leaf_index == self.leaf_index) & (public_data_read.value == self.value) + (public_data_read.leaf_slot == self.leaf_slot) & (public_data_read.value == self.value) } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_update_request.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_update_request.nr index 458d6ec9549b..675f9d405e8f 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_update_request.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_data_update_request.nr @@ -1,7 +1,7 @@ use dep::aztec::constants_gen::GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST; struct PublicDataUpdateRequest { - leaf_index : Field, + leaf_slot : Field, old_value : Field, new_value : Field } @@ -9,26 +9,26 @@ struct PublicDataUpdateRequest { impl PublicDataUpdateRequest { pub fn empty() -> Self { Self { - leaf_index : 0, + leaf_slot : 0, old_value : 0, new_value : 0 } } pub fn eq(self, update_request: PublicDataUpdateRequest) -> bool { - (update_request.leaf_index == self.leaf_index) & (update_request.old_value == self.old_value) + (update_request.leaf_slot == self.leaf_slot) & (update_request.old_value == self.old_value) & (update_request.new_value == self.new_value) } pub fn hash(self) -> Field { dep::std::hash::pedersen_hash_with_separator([ - self.leaf_index, + self.leaf_slot, self.old_value, self.new_value ], GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST) } pub fn is_empty(self) -> bool { - (self.leaf_index == 0) & (self.old_value == 0) & (self.new_value == 0) + (self.leaf_slot == 0) & (self.old_value == 0) & (self.new_value == 0) } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr index d41008a7ad48..a9305d630316 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr @@ -81,7 +81,7 @@ impl PreviousKernelDataBuilder { if i as u64 < num_updates as u64 { let update_request = PublicDataUpdateRequest { // The default leaf index is its index + 23. - leaf_index: value_offset + i + 23, + leaf_slot: value_offset + i + 23, // The default value is its index + 45. old_value: value_offset + i + 45, // The default value is its index + 678. @@ -98,7 +98,7 @@ impl PreviousKernelDataBuilder { if i as u64 < num_reads as u64 { let read_request = PublicDataRead { // The default leaf index is its index + 34. - leaf_index: value_offset + i + 34, + leaf_slot: value_offset + i + 34, // The default value is its index + 5566. value: value_offset + i + 5566, }; diff --git a/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts b/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts index f189d5926886..2eb6fcf4cc40 100644 --- a/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts +++ b/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts @@ -111,9 +111,13 @@ function generateStructInterfaces(type: ABIType, output: Set): string { let result = ''; // Edge case to handle the array of structs case. - if (type.kind === 'array' && type.type.kind === 'struct' && !output.has(getLastComponentOfPath(type.type.path))) { + if ( + type.kind === 'array' && + ((type.type.kind === 'struct' && !output.has(getLastComponentOfPath(type.type.path))) || type.type.kind === 'array') + ) { result += generateStructInterfaces(type.type, output); } + if (type.kind !== 'struct') { return result; } diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index 3ff307a60344..addce00443b5 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -43,6 +43,7 @@ import { NewContractData, NullifierLeafPreimage, OptionallyRevealedData, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, Point, PreviousKernelData, @@ -57,6 +58,8 @@ import { PublicCallStackItem, PublicCircuitPublicInputs, PublicDataRead, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, PublicDataUpdateRequest, PublicKernelInputs, ReadRequestMembershipWitness, @@ -118,6 +121,9 @@ import { BlocksTreeRootMembershipWitness as BlocksTreeRootMembershipWitnessNoir, NullifierLeafPreimage as NullifierLeafPreimageNoir, NullifierMembershipWitness as NullifierMembershipWitnessNoir, + PublicDataMembershipWitness as PublicDataMembershipWitnessNoir, + PublicDataTreeLeaf as PublicDataTreeLeafNoir, + PublicDataTreeLeafPreimage as PublicDataTreeLeafPreimageNoir, } from './types/rollup_base_types.js'; import { MergeRollupInputs as MergeRollupInputsNoir } from './types/rollup_merge_types.js'; import { @@ -162,7 +168,7 @@ export function mapNumberFromNoir(number: NoirField): number { /** * */ -export function mapNumberToNoir(number: number): NoirField { +export function mapNumberToNoir(number: bigint): NoirField { return new Fr(BigInt(number)).toString(); } @@ -683,7 +689,7 @@ export function mapPublicDataUpdateRequestFromNoir( publicDataUpdateRequest: PublicDataUpdateRequestNoir, ): PublicDataUpdateRequest { return new PublicDataUpdateRequest( - mapFieldFromNoir(publicDataUpdateRequest.leaf_index), + mapFieldFromNoir(publicDataUpdateRequest.leaf_slot), mapFieldFromNoir(publicDataUpdateRequest.old_value), mapFieldFromNoir(publicDataUpdateRequest.new_value), ); @@ -698,7 +704,7 @@ export function mapPublicDataUpdateRequestToNoir( publicDataUpdateRequest: PublicDataUpdateRequest, ): PublicDataUpdateRequestNoir { return { - leaf_index: mapFieldToNoir(publicDataUpdateRequest.leafIndex), + leaf_slot: mapFieldToNoir(publicDataUpdateRequest.leafSlot), old_value: mapFieldToNoir(publicDataUpdateRequest.oldValue), new_value: mapFieldToNoir(publicDataUpdateRequest.newValue), }; @@ -710,7 +716,7 @@ export function mapPublicDataUpdateRequestToNoir( * @returns The parsed public data read. */ export function mapPublicDataReadFromNoir(publicDataRead: PublicDataReadNoir): PublicDataRead { - return new PublicDataRead(mapFieldFromNoir(publicDataRead.leaf_index), mapFieldFromNoir(publicDataRead.value)); + return new PublicDataRead(mapFieldFromNoir(publicDataRead.leaf_slot), mapFieldFromNoir(publicDataRead.value)); } /** @@ -720,7 +726,7 @@ export function mapPublicDataReadFromNoir(publicDataRead: PublicDataReadNoir): P */ export function mapPublicDataReadToNoir(publicDataRead: PublicDataRead): PublicDataReadNoir { return { - leaf_index: mapFieldToNoir(publicDataRead.leafIndex), + leaf_slot: mapFieldToNoir(publicDataRead.leafSlot), value: mapFieldToNoir(publicDataRead.value), }; } @@ -1305,7 +1311,7 @@ export function mapNullifierLeafPreimageToNoir( return { leaf_value: mapFieldToNoir(nullifierLeafPreimage.nullifier), next_value: mapFieldToNoir(nullifierLeafPreimage.nextNullifier), - next_index: mapNumberToNoir(Number(nullifierLeafPreimage.nextIndex)), + next_index: mapNumberToNoir(nullifierLeafPreimage.nextIndex), }; } @@ -1323,6 +1329,18 @@ export function mapNullifierMembershipWitnessToNoir( }; } +/** + * + */ +export function mapPublicDataMembershipWitnessToNoir( + membershipWitness: MembershipWitness, +): PublicDataMembershipWitnessNoir { + return { + leaf_index: membershipWitness.leafIndex.toString(), + sibling_path: mapTuple(membershipWitness.siblingPath, mapFieldToNoir), + }; +} + /** * Maps a membership witness of the blocks tree to noir. * @param membershipWitness - The membership witness. @@ -1337,6 +1355,28 @@ export function mapBlocksTreeRootMembershipWitnessToNoir( }; } +/** + * + */ +export function mapPublicDataTreeLeafToNoir(leaf: PublicDataTreeLeaf): PublicDataTreeLeafNoir { + return { + slot: mapFieldToNoir(leaf.slot), + value: mapFieldToNoir(leaf.value), + }; +} + +/** + * + */ +export function mapPublicDataTreePreimageToNoir(preimage: PublicDataTreeLeafPreimage): PublicDataTreeLeafPreimageNoir { + return { + slot: mapFieldToNoir(preimage.slot), + value: mapFieldToNoir(preimage.value), + next_slot: mapFieldToNoir(preimage.nextSlot), + next_index: mapNumberToNoir(preimage.nextIndex), + }; +} + /** * Maps the inputs to the base rollup to noir. * @param input - The circuits.js base rollup inputs. @@ -1351,7 +1391,9 @@ export function mapBaseRollupInputsToNoir(inputs: BaseRollupInputs): BaseRollupI start_public_data_tree_root: mapFieldToNoir(inputs.startPublicDataTreeRoot), start_blocks_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startBlocksTreeSnapshot), sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapFieldToNoir), - sorted_new_nullifiers_indexes: mapTuple(inputs.sortednewNullifiersIndexes, mapNumberToNoir), + sorted_new_nullifiers_indexes: mapTuple(inputs.sortednewNullifiersIndexes, (index: number) => + mapNumberToNoir(BigInt(index)), + ), low_nullifier_leaf_preimages: mapTuple(inputs.lowNullifierLeafPreimages, mapNullifierLeafPreimageToNoir), low_nullifier_membership_witness: mapTuple( inputs.lowNullifierMembershipWitness, @@ -1359,15 +1401,63 @@ export function mapBaseRollupInputsToNoir(inputs: BaseRollupInputs): BaseRollupI ), new_commitments_subtree_sibling_path: mapTuple(inputs.newCommitmentsSubtreeSiblingPath, mapFieldToNoir), new_nullifiers_subtree_sibling_path: mapTuple(inputs.newNullifiersSubtreeSiblingPath, mapFieldToNoir), + public_data_writes_subtree_sibling_paths: mapTuple( + inputs.publicDataWritesSubtreeSiblingPaths, + (txPath: Tuple) => { + return mapTuple(txPath, mapFieldToNoir); + }, + ), new_contracts_subtree_sibling_path: mapTuple(inputs.newContractsSubtreeSiblingPath, mapFieldToNoir), - new_public_data_update_requests_sibling_paths: mapTuple( - inputs.newPublicDataUpdateRequestsSiblingPaths, - (siblingPath: Tuple) => mapTuple(siblingPath, mapFieldToNoir), + + sorted_public_data_writes: mapTuple( + inputs.sortedPublicDataWrites, + (kernelWrites: Tuple) => { + return mapTuple(kernelWrites, mapPublicDataTreeLeafToNoir); + }, + ), + + sorted_public_data_writes_indexes: mapTuple( + inputs.sortedPublicDataWritesIndexes, + (txIndexes: Tuple) => { + return mapTuple(txIndexes, (index: number) => mapNumberToNoir(BigInt(index))); + }, ), - new_public_data_reads_sibling_paths: mapTuple( - inputs.newPublicDataReadsSiblingPaths, - (siblingPath: Tuple) => mapTuple(siblingPath, mapFieldToNoir), + + low_public_data_writes_preimages: mapTuple( + inputs.lowPublicDataWritesPreimages, + (txPreimages: Tuple) => { + return mapTuple(txPreimages, mapPublicDataTreePreimageToNoir); + }, ), + + low_public_data_writes_witnesses: mapTuple( + inputs.lowPublicDataWritesMembershipWitnesses, + ( + txWitnesses: Tuple< + MembershipWitness, + typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, + ) => { + return mapTuple(txWitnesses, mapPublicDataMembershipWitnessToNoir); + }, + ), + + public_data_reads_preimages: mapTuple( + inputs.publicDataReadsPreimages, + (txReadsPreimages: Tuple) => { + return mapTuple(txReadsPreimages, mapPublicDataTreePreimageToNoir); + }, + ), + + public_data_reads_witnesses: mapTuple( + inputs.publicDataReadsMembershipWitnesses, + ( + txReadsWitnesses: Tuple, typeof MAX_PUBLIC_DATA_READS_PER_TX>, + ) => { + return mapTuple(txReadsWitnesses, mapPublicDataMembershipWitnessToNoir); + }, + ), + blocks_tree_root_membership_witnesses: mapTuple( inputs.blocksTreeRootMembershipWitnesses, mapBlocksTreeRootMembershipWitnessToNoir, diff --git a/yarn-project/noir-protocol-circuits/src/types/private_kernel_init_types.ts b/yarn-project/noir-protocol-circuits/src/types/private_kernel_init_types.ts index d5beb0d9ab49..2d30efe6df8a 100644 --- a/yarn-project/noir-protocol-circuits/src/types/private_kernel_init_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/private_kernel_init_types.ts @@ -179,13 +179,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } diff --git a/yarn-project/noir-protocol-circuits/src/types/private_kernel_inner_types.ts b/yarn-project/noir-protocol-circuits/src/types/private_kernel_inner_types.ts index 2666c692e66c..c7d9691b7bfb 100644 --- a/yarn-project/noir-protocol-circuits/src/types/private_kernel_inner_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/private_kernel_inner_types.ts @@ -57,13 +57,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } diff --git a/yarn-project/noir-protocol-circuits/src/types/private_kernel_ordering_types.ts b/yarn-project/noir-protocol-circuits/src/types/private_kernel_ordering_types.ts index a84711e7fea4..72741eaec22b 100644 --- a/yarn-project/noir-protocol-circuits/src/types/private_kernel_ordering_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/private_kernel_ordering_types.ts @@ -57,13 +57,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } diff --git a/yarn-project/noir-protocol-circuits/src/types/public_kernel_private_previous_types.ts b/yarn-project/noir-protocol-circuits/src/types/public_kernel_private_previous_types.ts index 7690aca56f53..f773cc79c8ae 100644 --- a/yarn-project/noir-protocol-circuits/src/types/public_kernel_private_previous_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/public_kernel_private_previous_types.ts @@ -57,13 +57,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } diff --git a/yarn-project/noir-protocol-circuits/src/types/public_kernel_public_previous_types.ts b/yarn-project/noir-protocol-circuits/src/types/public_kernel_public_previous_types.ts index 739dce883791..9af3263a2ffe 100644 --- a/yarn-project/noir-protocol-circuits/src/types/public_kernel_public_previous_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/public_kernel_public_previous_types.ts @@ -57,13 +57,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } diff --git a/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts b/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts index 1c880cb2cdf8..2d7c5fe1427a 100644 --- a/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts @@ -57,13 +57,13 @@ export interface OptionallyRevealedData { } export interface PublicDataUpdateRequest { - leaf_index: Field; + leaf_slot: Field; old_value: Field; new_value: Field; } export interface PublicDataRead { - leaf_index: Field; + leaf_slot: Field; value: Field; } @@ -163,6 +163,23 @@ export interface NullifierMembershipWitness { sibling_path: FixedLengthArray; } +export interface PublicDataTreeLeaf { + slot: Field; + value: Field; +} + +export interface PublicDataTreeLeafPreimage { + slot: Field; + value: Field; + next_slot: Field; + next_index: u32; +} + +export interface PublicDataMembershipWitness { + leaf_index: Field; + sibling_path: FixedLengthArray; +} + export interface BlocksTreeRootMembershipWitness { leaf_index: Field; sibling_path: FixedLengthArray; @@ -197,9 +214,14 @@ export interface BaseRollupInputs { low_nullifier_membership_witness: FixedLengthArray; new_commitments_subtree_sibling_path: FixedLengthArray; new_nullifiers_subtree_sibling_path: FixedLengthArray; + public_data_writes_subtree_sibling_paths: FixedLengthArray, 2>; new_contracts_subtree_sibling_path: FixedLengthArray; - new_public_data_update_requests_sibling_paths: FixedLengthArray, 32>; - new_public_data_reads_sibling_paths: FixedLengthArray, 32>; + sorted_public_data_writes: FixedLengthArray, 2>; + sorted_public_data_writes_indexes: FixedLengthArray, 2>; + low_public_data_writes_preimages: FixedLengthArray, 2>; + low_public_data_writes_witnesses: FixedLengthArray, 2>; + public_data_reads_preimages: FixedLengthArray, 2>; + public_data_reads_witnesses: FixedLengthArray, 2>; blocks_tree_root_membership_witnesses: FixedLengthArray; constants: ConstantRollupData; } diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index 808e02f6cae5..c90467bcf115 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -134,7 +134,7 @@ describe('sequencer/solo_block_builder', () => { NULLIFIER_SUBTREE_HEIGHT, ); for (const write of txs.flatMap(tx => tx.data.end.publicDataUpdateRequests)) { - await expectsDb.updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, write.newValue.toBuffer(), write.leafIndex.value); + await expectsDb.updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, write.newValue.toBuffer(), write.leafSlot.value); } }; @@ -216,7 +216,7 @@ describe('sequencer/solo_block_builder', () => { n => new ContractData(n.contractAddress, n.portalContractAddress), ); const newPublicDataWrites = flatMap(txs, tx => - tx.data.end.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafIndex, t.newValue)), + tx.data.end.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafSlot, t.newValue)), ); const newL2ToL1Msgs = flatMap(txs, tx => tx.data.end.newL2ToL1Msgs); const newEncryptedLogs = new L2BlockL2Logs(txs.map(tx => tx.encryptedLogs || new TxL2Logs([]))); diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index f160b8dffe4c..0cb7bcda8252 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -10,9 +10,7 @@ import { L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, MAX_PUBLIC_DATA_READS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MembershipWitness, MergeRollupInputs, @@ -23,10 +21,14 @@ import { NULLIFIER_TREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NullifierLeafPreimage, + PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, PreviousKernelData, PreviousRollupData, Proof, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, ROLLUP_VK_TREE_HEIGHT, RollupTypes, RootRollupInputs, @@ -136,7 +138,7 @@ export class SoloBlockBuilder implements BlockBuilder { n => new ContractData(n.contractAddress, n.portalContractAddress), ); const newPublicDataWrites = flatMap(txs, tx => - tx.data.end.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafIndex, t.newValue)), + tx.data.end.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafSlot, t.newValue)), ); const newL2ToL1Msgs = flatMap(txs, tx => tx.data.end.newL2ToL1Msgs); @@ -602,40 +604,88 @@ export class SoloBlockBuilder implements BlockBuilder { } protected async processPublicDataUpdateRequests(tx: ProcessedTx) { - const newPublicDataUpdateRequestsSiblingPaths: Tuple< - Tuple, - typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - > = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero)); - for (const i in tx.data.end.publicDataUpdateRequests) { - const index = tx.data.end.publicDataUpdateRequests[i].leafIndex.value; - await this.db.updateLeaf( + const { lowLeavesWitnessData, newSubtreeSiblingPath, sortedNewLeaves, sortedNewLeavesIndexes } = + await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - tx.data.end.publicDataUpdateRequests[i].newValue.toBuffer(), - index, - ); - const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, index); - const array = path.toFieldArray(); - newPublicDataUpdateRequestsSiblingPaths[i] = makeTuple(PUBLIC_DATA_TREE_HEIGHT, j => - j < array.length ? array[j] : Fr.ZERO, + // TODO remove oldValue from update requests, it's not needed + tx.data.end.publicDataUpdateRequests.map(updateRequest => { + return new PublicDataTreeLeaf(updateRequest.leafSlot, updateRequest.newValue).toBuffer(); + }), + PUBLIC_DATA_SUBTREE_HEIGHT, ); + + if (lowLeavesWitnessData === undefined) { + throw new Error(`Could not craft public data batch insertion proofs`); } - return newPublicDataUpdateRequestsSiblingPaths; + + const sortedPublicDataWrites = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { + return PublicDataTreeLeaf.fromBuffer(sortedNewLeaves[i]); + }); + + const sortedPublicDataWritesIndexes = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { + return sortedNewLeavesIndexes[i]; + }); + + const subtreeSiblingPathAsFields = newSubtreeSiblingPath.toFieldArray(); + const newPublicDataSubtreeSiblingPath = makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, i => { + return subtreeSiblingPathAsFields[i]; + }); + + const lowPublicDataWritesMembershipWitnesses: Tuple< + MembershipWitness, + typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + > = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { + const witness = lowLeavesWitnessData[i]; + return MembershipWitness.fromBufferArray( + witness.index, + assertLength(witness.siblingPath.toBufferArray(), PUBLIC_DATA_TREE_HEIGHT), + ); + }); + + const lowPublicDataWritesPreimages: Tuple< + PublicDataTreeLeafPreimage, + typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + > = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { + return lowLeavesWitnessData[i].leafPreimage as PublicDataTreeLeafPreimage; + }); + + return { + lowPublicDataWritesPreimages, + lowPublicDataWritesMembershipWitnesses, + newPublicDataSubtreeSiblingPath, + sortedPublicDataWrites, + sortedPublicDataWritesIndexes, + }; } - protected async getPublicDataReadsSiblingPaths(tx: ProcessedTx) { - const newPublicDataReadsSiblingPaths: Tuple< - Tuple, + protected async getPublicDataReadsInfo(tx: ProcessedTx) { + const newPublicDataReadsWitnesses: Tuple< + MembershipWitness, typeof MAX_PUBLIC_DATA_READS_PER_TX - > = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero)); + > = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT, 0n)); + + const newPublicDataReadsPreimages: Tuple = + makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => PublicDataTreeLeafPreimage.empty()); for (const i in tx.data.end.publicDataReads) { - const index = tx.data.end.publicDataReads[i].leafIndex.value; - const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, index); + const leafSlot = tx.data.end.publicDataReads[i].leafSlot.value; + const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + if (!lowLeafResult) { + throw new Error(`Public data tree should have one initial leaf`); + } + const preimage = await this.db.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); + const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); const array = path.toFieldArray(); - newPublicDataReadsSiblingPaths[i] = makeTuple(PUBLIC_DATA_TREE_HEIGHT, j => - j < array.length ? array[j] : Fr.ZERO, + newPublicDataReadsWitnesses[i] = new MembershipWitness( + PUBLIC_DATA_TREE_HEIGHT, + BigInt(lowLeafResult.index), + makeTuple(PUBLIC_DATA_TREE_HEIGHT, i => array[i]), ); + newPublicDataReadsPreimages[i] = preimage! as PublicDataTreeLeafPreimage; } - return newPublicDataReadsSiblingPaths; + return { + newPublicDataReadsWitnesses, + newPublicDataReadsPreimages, + }; } // Builds the base rollup inputs, updating the contract, nullifier, and data trees in the process @@ -678,27 +728,11 @@ export class SoloBlockBuilder implements BlockBuilder { await this.db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, newCommitments); - // Update the public data tree and get membership witnesses. - // All public data reads are checked against the unmodified data root when the corresponding tx started, - // so it's the unmodified tree for tx1, and the one after applying tx1 update request for tx2. - // Update requests are checked against the tree as it is iteratively updated. - // See https://github.com/AztecProtocol/aztec3-packages/issues/270#issuecomment-1522258200 - const leftPublicDataReadSiblingPaths = await this.getPublicDataReadsSiblingPaths(left); - const leftPublicDataUpdateRequestsSiblingPaths = await this.processPublicDataUpdateRequests(left); - const rightPublicDataReadSiblingPaths = await this.getPublicDataReadsSiblingPaths(right); - const rightPublicDataUpdateRequestsSiblingPaths = await this.processPublicDataUpdateRequests(right); - - const newPublicDataReadsSiblingPaths = makeTuple(MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, i => - i < MAX_PUBLIC_DATA_READS_PER_TX - ? leftPublicDataReadSiblingPaths[i] - : rightPublicDataReadSiblingPaths[i - MAX_PUBLIC_DATA_READS_PER_TX], - ); - - const newPublicDataUpdateRequestsSiblingPaths = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, i => - i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - ? leftPublicDataUpdateRequestsSiblingPaths[i] - : rightPublicDataUpdateRequestsSiblingPaths[i - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - ); + // TODO explain this + const leftPublicDataReadsInfo = await this.getPublicDataReadsInfo(left); + const leftPublicDataUpdateRequestInfo = await this.processPublicDataUpdateRequests(left); + const rightPublicDataReadsInfo = await this.getPublicDataReadsInfo(right); + const rightPublicDataUpdateRequestInfo = await this.processPublicDataUpdateRequests(right); // Update the nullifier tree, capturing the low nullifier info for each individual operation const newNullifiers = [...left.data.end.newNullifiers, ...right.data.end.newNullifiers]; @@ -732,15 +766,46 @@ export class SoloBlockBuilder implements BlockBuilder { startNoteHashTreeSnapshot, startPublicDataTreeRoot: startPublicDataTreeSnapshot.root, startBlocksTreeSnapshot, + sortedPublicDataWrites: [ + leftPublicDataUpdateRequestInfo.sortedPublicDataWrites, + rightPublicDataUpdateRequestInfo.sortedPublicDataWrites, + ], + sortedPublicDataWritesIndexes: [ + leftPublicDataUpdateRequestInfo.sortedPublicDataWritesIndexes, + rightPublicDataUpdateRequestInfo.sortedPublicDataWritesIndexes, + ], + lowPublicDataWritesPreimages: [ + leftPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages, + rightPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages, + ], + lowPublicDataWritesMembershipWitnesses: [ + leftPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses, + rightPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses, + ], + publicDataWritesSubtreeSiblingPaths: [ + leftPublicDataUpdateRequestInfo.newPublicDataSubtreeSiblingPath, + rightPublicDataUpdateRequestInfo.newPublicDataSubtreeSiblingPath, + ], + sortedNewNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => Fr.fromBuffer(sortedNewNullifiers[i])), sortednewNullifiersIndexes: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => sortednewNullifiersIndexes[i]), newCommitmentsSubtreeSiblingPath, newContractsSubtreeSiblingPath, + newNullifiersSubtreeSiblingPath: makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, i => i < newNullifiersSubtreeSiblingPathArray.length ? newNullifiersSubtreeSiblingPathArray[i] : Fr.ZERO, ), - newPublicDataUpdateRequestsSiblingPaths, - newPublicDataReadsSiblingPaths, + + publicDataReadsPreimages: [ + leftPublicDataReadsInfo.newPublicDataReadsPreimages, + rightPublicDataReadsInfo.newPublicDataReadsPreimages, + ], + + publicDataReadsMembershipWitnesses: [ + leftPublicDataReadsInfo.newPublicDataReadsWitnesses, + rightPublicDataReadsInfo.newPublicDataReadsWitnesses, + ], + lowNullifierLeafPreimages: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i < nullifierWitnessLeaves.length ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage) diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 820da1cf0c67..9d52e68b8929 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -364,7 +364,7 @@ export class PublicProcessor { // Validate all items in enqueued public calls are in the kernel emitted stack const readsAreEqual = simPublicDataReads.reduce( (accum, read) => - accum && !!publicDataReads.find(item => item.leafIndex.equals(read.leafIndex) && item.value.equals(read.value)), + accum && !!publicDataReads.find(item => item.leafSlot.equals(read.leafSlot) && item.value.equals(read.value)), true, ); const updatesAreEqual = simPublicDataUpdateRequests.reduce( @@ -372,7 +372,7 @@ export class PublicProcessor { accum && !!publicDataUpdateRequests.find( item => - item.leafIndex.equals(update.leafIndex) && + item.leafSlot.equals(update.leafSlot) && item.oldValue.equals(update.oldValue) && item.newValue.equals(update.newValue), ), @@ -399,11 +399,11 @@ export class PublicProcessor { // most recently processed top/enqueued call. const numTotalReadsInKernel = arrayNonEmptyLength( publicInputs.end.publicDataReads, - f => f.leafIndex.equals(Fr.ZERO) && f.value.equals(Fr.ZERO), + f => f.leafSlot.equals(Fr.ZERO) && f.value.equals(Fr.ZERO), ); const numTotalUpdatesInKernel = arrayNonEmptyLength( publicInputs.end.publicDataUpdateRequests, - f => f.leafIndex.equals(Fr.ZERO) && f.oldValue.equals(Fr.ZERO) && f.newValue.equals(Fr.ZERO), + f => f.leafSlot.equals(Fr.ZERO) && f.oldValue.equals(Fr.ZERO) && f.newValue.equals(Fr.ZERO), ); const numReadsBeforeThisEnqueuedCall = numTotalReadsInKernel - simPublicDataReads.length; const numUpdatesBeforeThisEnqueuedCall = numTotalUpdatesInKernel - simPublicDataUpdateRequests.length; diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 40b8ab668f29..47dbba785ad2 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -8,12 +8,7 @@ import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; /** * Type alias for the nullifier tree ID. */ -export type IndexedTreeId = MerkleTreeId.NULLIFIER_TREE; - -/** - * Type alias for the public data tree ID. - */ -export type PublicTreeId = MerkleTreeId.PUBLIC_DATA_TREE; +export type IndexedTreeId = MerkleTreeId.NULLIFIER_TREE | MerkleTreeId.PUBLIC_DATA_TREE; /** * @@ -164,7 +159,7 @@ export interface MerkleTreeOperations { * @param leaf - The updated leaf value. * @param index - The index of the leaf to be updated. */ - updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: NullifierLeafPreimage | Buffer, index: bigint): Promise; + updateLeaf(treeId: IndexedTreeId, leaf: NullifierLeafPreimage | Buffer, index: bigint): Promise; /** * Returns the index containing a leaf value. diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 9a7f1ad629e2..925e55e0ef9c 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -10,6 +10,8 @@ import { NullifierLeaf, NullifierLeafPreimage, PUBLIC_DATA_TREE_HEIGHT, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { computeBlockHash, computeGlobalsHash } from '@aztec/circuits.js/abis'; import { Committable } from '@aztec/foundation/committable'; @@ -21,7 +23,6 @@ import { BatchInsertionResult, IndexedTree, Pedersen, - SparseTree, StandardIndexedTree, StandardTree, UpdateOnlyTree, @@ -41,7 +42,6 @@ import { IndexedTreeId, MerkleTreeDb, MerkleTreeOperations, - PublicTreeId, TreeInfo, } from './merkle_tree_db.js'; @@ -102,8 +102,10 @@ export class MerkleTrees implements MerkleTreeDb { `${MerkleTreeId[MerkleTreeId.NOTE_HASH_TREE]}`, NOTE_HASH_TREE_HEIGHT, ); - const publicDataTree: UpdateOnlyTree = await initializeTree( - treeBuilder(SparseTree), + const publicDataTree = await initializeTree( + (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { + return new StandardIndexedTree(db, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf); + }, this.db, hasher, `${MerkleTreeId[MerkleTreeId.PUBLIC_DATA_TREE]}`, @@ -374,7 +376,7 @@ export class MerkleTrees implements MerkleTreeDb { * @param index - The index to insert into. * @returns Empty promise. */ - public async updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: Buffer, index: bigint): Promise { + public async updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { return await this.synchronize(() => this._updateLeaf(treeId, leaf, index)); } @@ -487,7 +489,7 @@ export class MerkleTrees implements MerkleTreeDb { return await tree.appendLeaves(leaves); } - private async _updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: Buffer, index: bigint): Promise { + private async _updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { const tree = this.trees[treeId]; if (!('updateLeaf' in tree)) { throw new Error('Tree does not support `updateLeaf` method'); From a7c2148bb0dacc67df6f5689422779ab22c07c83 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 4 Dec 2023 17:36:08 +0000 Subject: [PATCH 16/40] chore: update constants gen --- l1-contracts/src/core/libraries/ConstantsGen.sol | 4 +++- yarn-project/circuits.js/src/constants.gen.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 436e18e84b26..e092f5f561d3 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -47,7 +47,7 @@ library Constants { uint256 internal constant FUNCTION_TREE_HEIGHT = 5; uint256 internal constant CONTRACT_TREE_HEIGHT = 16; uint256 internal constant NOTE_HASH_TREE_HEIGHT = 32; - uint256 internal constant PUBLIC_DATA_TREE_HEIGHT = 254; + uint256 internal constant PUBLIC_DATA_TREE_HEIGHT = 40; uint256 internal constant NULLIFIER_TREE_HEIGHT = 20; uint256 internal constant L1_TO_L2_MSG_TREE_HEIGHT = 16; uint256 internal constant ROLLUP_VK_TREE_HEIGHT = 8; @@ -56,8 +56,10 @@ library Constants { uint256 internal constant NOTE_HASH_SUBTREE_HEIGHT = 7; uint256 internal constant NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH = 25; uint256 internal constant NULLIFIER_SUBTREE_HEIGHT = 7; + uint256 internal constant PUBLIC_DATA_SUBTREE_HEIGHT = 4; uint256 internal constant BLOCKS_TREE_HEIGHT = 16; uint256 internal constant NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH = 13; + uint256 internal constant PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 36; uint256 internal constant L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; uint256 internal constant L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12; uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 0a9367c62fd7..856df947db42 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -29,13 +29,11 @@ export const KERNELS_PER_BASE_ROLLUP = 2; export const MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP = 128; export const MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP = 32; export const MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP = 32; -export const PUBLIC_DATA_SUBTREE_HEIGHT = 4; export const VK_TREE_HEIGHT = 3; export const FUNCTION_TREE_HEIGHT = 5; export const CONTRACT_TREE_HEIGHT = 16; export const NOTE_HASH_TREE_HEIGHT = 32; export const PUBLIC_DATA_TREE_HEIGHT = 40; -export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 36; export const NULLIFIER_TREE_HEIGHT = 20; export const L1_TO_L2_MSG_TREE_HEIGHT = 16; export const ROLLUP_VK_TREE_HEIGHT = 8; @@ -44,8 +42,10 @@ export const CONTRACT_SUBTREE_SIBLING_PATH_LENGTH = 15; export const NOTE_HASH_SUBTREE_HEIGHT = 7; export const NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH = 25; export const NULLIFIER_SUBTREE_HEIGHT = 7; +export const PUBLIC_DATA_SUBTREE_HEIGHT = 4; export const BLOCKS_TREE_HEIGHT = 16; export const NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH = 13; +export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 36; export const L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; export const L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12; export const FUNCTION_SELECTOR_NUM_BYTES = 4; From 1d8566610439506b014721bcd5b79935c9b4df52 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 09:15:50 +0000 Subject: [PATCH 17/40] feat: more work towards switching data tree --- .../src/client/view_data_oracle.ts | 2 +- .../base_or_merge_rollup_public_inputs.ts | 16 +- .../src/structs/rollup/base_rollup.ts | 6 +- .../src/structs/rollup/root_rollup.ts | 16 +- .../circuits.js/src/tests/factories.ts | 8 +- .../base_or_merge_rollup_public_inputs.nr | 4 +- .../src/abis/public_data_tree_leaf.nr | 3 + .../rollup-lib/src/base/base_rollup_inputs.nr | 189 ++++++++++++------ .../src/crates/rollup-lib/src/components.nr | 2 +- .../src/merge/merge_rollup_inputs.nr | 12 +- .../src/crates/rollup-lib/src/root.nr | 14 +- .../src/root/root_rollup_public_inputs.nr | 4 +- .../src/tests/previous_rollup_data.nr | 10 +- .../noir-protocol-circuits/src/index.ts | 1 + .../src/type_conversion.ts | 18 +- .../src/types/rollup_base_types.ts | 6 +- .../src/types/rollup_merge_types.ts | 4 +- .../src/types/rollup_root_types.ts | 8 +- .../pxe/src/synchronizer/synchronizer.ts | 2 +- .../block_builder/solo_block_builder.test.ts | 12 +- .../src/block_builder/solo_block_builder.ts | 10 +- yarn-project/types/src/l2_block.ts | 50 ++--- .../server_world_state_synchronizer.test.ts | 4 +- .../src/world-state-db/merkle_trees.ts | 2 +- 24 files changed, 243 insertions(+), 160 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index f744e54dd6c2..2f057bf0780f 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -108,7 +108,7 @@ export class ViewDataOracle extends TypedOracle { block.endL1ToL2MessagesTreeSnapshot.root, block.endBlocksTreeSnapshot.root, new Fr(0), // TODO(#3441) privateKernelVkTreeRoot is not present in L2Block and it's not yet populated in noir - block.endPublicDataTreeRoot, + block.endPublicDataTreeSnapshot.root, computeGlobalsHash(block.globalVariables), ); } diff --git a/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts index c58f778a9948..a5e94ac50232 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts @@ -60,13 +60,13 @@ export class BaseOrMergeRollupPublicInputs { public endContractTreeSnapshot: AppendOnlyTreeSnapshot, /** - * Root of the public data tree at the start of the rollup circuit. + * Snapshot of the public data tree at the start of the rollup circuit. */ - public startPublicDataTreeRoot: Fr, + public startPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** - * Root of the public data tree at the end of the rollup circuit. + * Snapshot of the public data tree at the end of the rollup circuit. */ - public endPublicDataTreeRoot: Fr, + public endPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** * SHA256 hashes of calldata. Used to make public inputs constant-sized (to then be unpacked on-chain). @@ -94,8 +94,8 @@ export class BaseOrMergeRollupPublicInputs { reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), + reader.readObject(AppendOnlyTreeSnapshot), + reader.readObject(AppendOnlyTreeSnapshot), reader.readArray(NUM_FIELDS_PER_SHA256, Fr) as [Fr, Fr], ); } @@ -120,8 +120,8 @@ export class BaseOrMergeRollupPublicInputs { this.startContractTreeSnapshot, this.endContractTreeSnapshot, - this.startPublicDataTreeRoot, - this.endPublicDataTreeRoot, + this.startPublicDataTreeSnapshot, + this.endPublicDataTreeSnapshot, this.calldataHash, ); diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts index 3de5dac8433b..72b62c72e422 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup.ts @@ -112,9 +112,9 @@ export class BaseRollupInputs { */ public startContractTreeSnapshot: AppendOnlyTreeSnapshot, /** - * Root of the public data tree at the start of the base rollup circuit. + * Snapshot of the public data tree at the start of the base rollup circuit. */ - public startPublicDataTreeRoot: Fr, + public startPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** * Snapshot of the blocks tree at the start of the base rollup circuit. */ @@ -232,7 +232,7 @@ export class BaseRollupInputs { fields.startNoteHashTreeSnapshot, fields.startNullifierTreeSnapshot, fields.startContractTreeSnapshot, - fields.startPublicDataTreeRoot, + fields.startPublicDataTreeSnapshot, fields.startBlocksTreeSnapshot, fields.sortedNewNullifiers, fields.sortednewNullifiersIndexes, diff --git a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts index 087accf15261..cc57dc45e126 100644 --- a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts @@ -111,13 +111,13 @@ export class RootRollupPublicInputs { public endContractTreeSnapshot: AppendOnlyTreeSnapshot, /** - * Root of the public data tree at the start of the rollup. + * Snapshot of the public data tree at the start of the rollup. */ - public startPublicDataTreeRoot: Fr, + public startPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** - * Root of the public data tree at the end of the rollup. + * Snapshot of the public data tree at the end of the rollup. */ - public endPublicDataTreeRoot: Fr, + public endPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** * Snapshot of the historical note hash tree roots tree at the start of the rollup. @@ -184,8 +184,8 @@ export class RootRollupPublicInputs { fields.endNullifierTreeSnapshot, fields.startContractTreeSnapshot, fields.endContractTreeSnapshot, - fields.startPublicDataTreeRoot, - fields.endPublicDataTreeRoot, + fields.startPublicDataTreeSnapshot, + fields.endPublicDataTreeSnapshot, fields.startTreeOfHistoricalNoteHashTreeRootsSnapshot, fields.endTreeOfHistoricalNoteHashTreeRootsSnapshot, fields.startTreeOfHistoricalContractTreeRootsSnapshot, @@ -242,8 +242,8 @@ export class RootRollupPublicInputs { reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), + reader.readObject(AppendOnlyTreeSnapshot), + reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 19258cea48ec..f93c78ce85f2 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -802,8 +802,8 @@ export function makeBaseOrMergeRollupPublicInputs( makeAppendOnlyTreeSnapshot(seed + 0x600), makeAppendOnlyTreeSnapshot(seed + 0x700), makeAppendOnlyTreeSnapshot(seed + 0x800), - fr(seed + 0x900), - fr(seed + 0x1000), + makeAppendOnlyTreeSnapshot(seed + 0x900), + makeAppendOnlyTreeSnapshot(seed + 0x1000), [fr(seed + 0x901), fr(seed + 0x902)], ); } @@ -864,8 +864,8 @@ export function makeRootRollupPublicInputs( endNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), startContractTreeSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), endContractTreeSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), - startPublicDataTreeRoot: fr((seed += 0x100)), - endPublicDataTreeRoot: fr((seed += 0x100)), + startPublicDataTreeSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), + endPublicDataTreeSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), startTreeOfHistoricalNoteHashTreeRootsSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), endTreeOfHistoricalNoteHashTreeRootsSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), startTreeOfHistoricalContractTreeRootsSnapshot: makeAppendOnlyTreeSnapshot((seed += 0x100)), diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr index 54546a9e9fbc..ed3e4cfe009b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr @@ -26,8 +26,8 @@ struct BaseOrMergeRollupPublicInputs { start_contract_tree_snapshot : AppendOnlyTreeSnapshot, end_contract_tree_snapshot : AppendOnlyTreeSnapshot, - start_public_data_tree_root : Field, - end_public_data_tree_root : Field, + start_public_data_tree_snapshot : AppendOnlyTreeSnapshot, + end_public_data_tree_snapshot : AppendOnlyTreeSnapshot, // We hash public inputs to make them constant-sized (to then be unpacked on-chain) // U128 isn't safe if it's an input to the circuit (it won't automatically constrain the witness) diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr index e3a6e7e3658c..31e800793615 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr @@ -46,4 +46,7 @@ impl PublicDataTreeLeaf { (self.slot == 0) & (self.value == 0) } + pub fn eq(self, other: Self) -> bool { + (self.slot == other.slot) & (self.value == other.value) + } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index 89edde91fa5d..2bc0999fefbb 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -32,6 +32,7 @@ use dep::aztec::constants_gen::{ NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, + PUBLIC_DATA_SUBTREE_HEIGHT, }; use dep::types::abis::previous_kernel_data::PreviousKernelData; use dep::types::abis::membership_witness::{NullifierMembershipWitness, PublicDataMembershipWitness, MembershipWitness}; @@ -42,7 +43,7 @@ struct BaseRollupInputs { start_note_hash_tree_snapshot: AppendOnlyTreeSnapshot, start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot, start_contract_tree_snapshot: AppendOnlyTreeSnapshot, - start_public_data_tree_root: Field, + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot, start_blocks_tree_snapshot: AppendOnlyTreeSnapshot, sorted_new_nullifiers: [Field; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], @@ -116,7 +117,7 @@ impl BaseRollupInputs { let end_nullifier_tree_snapshot = self.check_nullifier_tree_non_membership_and_insert_to_tree(); // Validate public public data reads and public data update requests, and update public data tree - let end_public_data_tree_root = self.validate_and_process_public_state(); + let end_public_data_tree_snapshot = self.validate_and_process_public_state(); // Calculate the overall calldata hash let calldata_hash = BaseRollupInputs::components_compute_kernel_calldata_hash(self.kernel_data); @@ -137,8 +138,8 @@ impl BaseRollupInputs { end_nullifier_tree_snapshot : end_nullifier_tree_snapshot, start_contract_tree_snapshot : self.start_contract_tree_snapshot, end_contract_tree_snapshot : end_contract_tree_snapshot, - start_public_data_tree_root : self.start_public_data_tree_root, - end_public_data_tree_root : end_public_data_tree_root, + start_public_data_tree_snapshot : self.start_public_data_tree_snapshot, + end_public_data_tree_snapshot: end_public_data_tree_snapshot, calldata_hash : calldata_hash, } } @@ -249,48 +250,64 @@ impl BaseRollupInputs { calculate_subtree(leaves.map(|leaf:NullifierLeafPreimage| leaf.hash())) } - fn validate_and_process_public_state(self) -> Field { - // // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // // Blocks all interesting usecases that read and write to the same public state in the same tx. - // // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - - - // // Process public data reads and public data update requests for left input - // // validate_public_data_reads( - // // self.start_public_data_tree_root, - // // self.kernel_data[0].public_inputs.end.public_data_reads, - // // 0, - // // self.new_public_data_reads_sibling_paths); - - // let mid_public_data_tree_root = insert_public_data_update_requests( - // self.start_public_data_tree_root, - // self.kernel_data[0].public_inputs.end.public_data_update_requests, - // 0, - // self.new_public_data_update_requests_sibling_paths - // ); + fn validate_and_process_public_state(self) -> AppendOnlyTreeSnapshot { + // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. + // Blocks all interesting usecases that read and write to the same public state in the same tx. + // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 + + + // Process public data reads and public data update requests for left input + // validate_public_data_reads( + // self.start_public_data_tree_root, + // self.kernel_data[0].public_inputs.end.public_data_reads, + // 0, + // self.new_public_data_reads_sibling_paths); + + let mid_public_data_tree_snapshot = insert_public_data_update_requests( + self.start_public_data_tree_snapshot, + self.kernel_data[0].public_inputs.end.public_data_update_requests.map(|request: PublicDataUpdateRequest| { + PublicDataTreeLeaf { + slot: request.leaf_slot, + value: request.new_value, + } + }), + self.sorted_public_data_writes[0], + self.sorted_public_data_writes_indexes[0], + self.low_public_data_writes_preimages[0], + self.low_public_data_writes_witnesses[0], + self.public_data_writes_subtree_sibling_paths[0], + ); - // // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // // Blocks all interesting usecases that read and write to the same public state in the same tx. - // // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 + // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. + // Blocks all interesting usecases that read and write to the same public state in the same tx. + // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - // // Process public data reads and public data update requests for right input using the resulting tree root from the - // // left one - // // validate_public_data_reads( - // // mid_public_data_tree_root, - // // baseRollupInputs.kernel_data[1].public_inputs.end.public_data_reads, - // // MAX_PUBLIC_DATA_READS_PER_TX, - // // baseRollupInputs.new_public_data_reads_sibling_paths); + // Process public data reads and public data update requests for right input using the resulting tree root from the + // left one + // validate_public_data_reads( + // mid_public_data_tree_root, + // baseRollupInputs.kernel_data[1].public_inputs.end.public_data_reads, + // MAX_PUBLIC_DATA_READS_PER_TX, + // baseRollupInputs.new_public_data_reads_sibling_paths); - // let end_public_data_tree_root = insert_public_data_update_requests( - // mid_public_data_tree_root, - // self.kernel_data[1].public_inputs.end.public_data_update_requests, - // MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - // self.new_public_data_update_requests_sibling_paths - // ); + let end_public_data_tree_snapshot = insert_public_data_update_requests( + mid_public_data_tree_snapshot, + self.kernel_data[1].public_inputs.end.public_data_update_requests.map(|request: PublicDataUpdateRequest| { + PublicDataTreeLeaf { + slot: request.leaf_slot, + value: request.new_value, + } + }), + self.sorted_public_data_writes[1], + self.sorted_public_data_writes_indexes[1], + self.low_public_data_writes_preimages[1], + self.low_public_data_writes_witnesses[1], + self.public_data_writes_subtree_sibling_paths[1], + ); - self.start_public_data_tree_root + end_public_data_tree_snapshot } // Computes the calldata hash for a base rollup @@ -421,26 +438,78 @@ fn verify_kernel_proof(proof: Proof) -> bool { true } -// fn insert_public_data_update_requests( -// tree_root: Field, -// public_data_update_requests: [PublicDataUpdateRequest;MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], -// witnesses_offset: Field, -// witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] -// ) -> Field { -// let mut root = tree_root; - -// for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { -// let state_write = public_data_update_requests[i]; -// let witness = witnesses[i + witnesses_offset]; - -// if (!state_write.is_empty()) { -// components::assert_check_membership(state_write.old_value, state_write.leaf_index, witness, root); -// root = components::root_from_sibling_path(state_write.new_value, state_write.leaf_index, witness); -// } -// } - -// root -// } +fn insert_public_data_update_requests( + prev_snapshot: AppendOnlyTreeSnapshot, + public_data_writes: [PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_writes: [PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_writes_indexes: [u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], +) -> AppendOnlyTreeSnapshot { + crate::indexed_tree::batch_insert( + prev_snapshot, + public_data_writes, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + public_data_writes_subtree_sibling_path, + low_public_data_writes_preimages, + low_public_data_writes_witnesses.map(|witness: PublicDataMembershipWitness| { + MembershipWitness { + leaf_index: witness.leaf_index, + sibling_path: witness.sibling_path, + } + }), + |a: PublicDataTreeLeaf, b: PublicDataTreeLeaf| a.eq(b), // PublicDataTreeLeaf equals + |write: PublicDataTreeLeaf| write.is_empty(), // PublicDataTreeLeaf is_empty + |preimage: PublicDataTreeLeafPreimage| preimage.hash(), // Hash preimage + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { // Is valid low preimage + let is_update = low_preimage.slot == write.slot; + let is_low_empty = low_preimage.is_empty(); + + let is_less_than_nullifier = full_field_less_than(low_preimage.slot, write.slot); + let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); + let is_in_range = is_less_than_nullifier & ( + is_next_greater_than | + ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0))); + + (!is_low_empty) & (is_update | is_in_range) + }, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { // Update low leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage{ + slot : low_preimage.slot, + value: write.value, + next_slot : low_preimage.next_slot, + next_index : low_preimage.next_index, + } + } else { + PublicDataTreeLeafPreimage{ + slot : low_preimage.slot, + value: low_preimage.value, + next_slot : write.slot, + next_index : write_index, + } + } + }, + |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { // Build insertion leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage::default() + }else { + PublicDataTreeLeafPreimage { + slot: write.slot, + value: write.value, + next_slot : low_preimage.next_slot, + next_index : low_preimage.next_index, + } + } + }, + [0; PUBLIC_DATA_SUBTREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT], + ) +} // fn validate_public_data_reads( // tree_root: Field, diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/components.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/components.nr index 4eb6d8bffd85..7c3f945ecf69 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/components.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/components.nr @@ -51,7 +51,7 @@ pub fn assert_prev_rollups_follow_on_from_each_other(left : BaseOrMergeRollupPub assert(left.end_note_hash_tree_snapshot.eq(right.start_note_hash_tree_snapshot), "input proofs have different note hash tree snapshots"); assert(left.end_nullifier_tree_snapshot.eq(right.start_nullifier_tree_snapshot),"input proofs have different nullifier tree snapshots"); assert(left.end_contract_tree_snapshot.eq(right.start_contract_tree_snapshot), "input proofs have different contract tree snapshots"); - assert(left.end_public_data_tree_root == right.start_public_data_tree_root, "input proofs have different public data tree snapshots"); + assert(left.end_public_data_tree_snapshot.eq(right.start_public_data_tree_snapshot), "input proofs have different public data tree snapshots"); } /** diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/merge/merge_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/merge/merge_rollup_inputs.nr index 249515b57155..eb82cb61e35f 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/merge/merge_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/merge/merge_rollup_inputs.nr @@ -39,8 +39,8 @@ impl MergeRollupInputs { end_nullifier_tree_snapshot : right.end_nullifier_tree_snapshot, start_contract_tree_snapshot : left.start_contract_tree_snapshot, end_contract_tree_snapshot : right.end_contract_tree_snapshot, - start_public_data_tree_root : left.start_public_data_tree_root, - end_public_data_tree_root : right.end_public_data_tree_root, + start_public_data_tree_snapshot : left.start_public_data_tree_snapshot, + end_public_data_tree_snapshot : right.end_public_data_tree_snapshot, calldata_hash : new_calldata_hash, }; @@ -158,11 +158,11 @@ mod tests { outputs.end_contract_tree_snapshot.eq(inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot) ); - assert_eq( - outputs.start_public_data_tree_root, inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_root + assert( + outputs.start_public_data_tree_snapshot.eq(inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot) ); - assert_eq( - outputs.end_public_data_tree_root, inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_root + assert( + outputs.end_public_data_tree_snapshot.eq(inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot) ); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr index d6052cee5e24..2fa36d0447c8 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr @@ -43,7 +43,7 @@ impl RootRollupInputs { right.end_nullifier_tree_snapshot.root, right.end_contract_tree_snapshot.root, new_l1_to_l2_messages_tree_snapshot.root, - right.end_public_data_tree_root); + right.end_public_data_tree_snapshot.root); // Update the blocks tree let end_blocks_tree_snapshot = components::insert_subtree_to_snapshot_tree( @@ -68,8 +68,8 @@ impl RootRollupInputs { end_nullifier_tree_snapshot : right.end_nullifier_tree_snapshot, start_contract_tree_snapshot : left.start_contract_tree_snapshot, end_contract_tree_snapshot : right.end_contract_tree_snapshot, - start_public_data_tree_root : left.start_public_data_tree_root, - end_public_data_tree_root : right.end_public_data_tree_root, + start_public_data_tree_snapshot : left.start_public_data_tree_snapshot, + end_public_data_tree_snapshot : right.end_public_data_tree_snapshot, start_l1_to_l2_messages_tree_snapshot : self.start_l1_to_l2_messages_tree_snapshot, end_l1_to_l2_messages_tree_snapshot : new_l1_to_l2_messages_tree_snapshot, start_blocks_tree_snapshot : self.start_blocks_tree_snapshot, @@ -196,11 +196,11 @@ mod tests { outputs.end_contract_tree_snapshot.eq(inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot) ); - assert_eq( - outputs.start_public_data_tree_root, inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_root + assert( + outputs.start_public_data_tree_snapshot.eq(inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot) ); - assert_eq( - outputs.end_public_data_tree_root, inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_root + assert( + outputs.end_public_data_tree_snapshot.eq(inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot) ); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root/root_rollup_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root/root_rollup_public_inputs.nr index 9b15ca2955f0..029c0dd1dc40 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root/root_rollup_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root/root_rollup_public_inputs.nr @@ -23,8 +23,8 @@ struct RootRollupPublicInputs { start_contract_tree_snapshot : AppendOnlyTreeSnapshot, end_contract_tree_snapshot : AppendOnlyTreeSnapshot, - start_public_data_tree_root : Field, - end_public_data_tree_root : Field, + start_public_data_tree_snapshot : AppendOnlyTreeSnapshot, + end_public_data_tree_snapshot : AppendOnlyTreeSnapshot, start_tree_of_historical_note_hash_tree_roots_snapshot : AppendOnlyTreeSnapshot, end_tree_of_historical_note_hash_tree_roots_snapshot : AppendOnlyTreeSnapshot, diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr index fb3fd0a5eb8e..35016f874f5b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr @@ -56,8 +56,14 @@ pub fn default_previous_rollup_data() -> [PreviousRollupData; 2] { next_available_leaf_index: 2 }; - previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_public_data_tree_root = 3; - previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_public_data_tree_root = 3; + previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: 3, + next_available_leaf_index: 4 + }; + previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot =AppendOnlyTreeSnapshot { + root: 4, + next_available_leaf_index: 5 + }; previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_type = BASE_ROLLUP_TYPE; previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_type = BASE_ROLLUP_TYPE; diff --git a/yarn-project/noir-protocol-circuits/src/index.ts b/yarn-project/noir-protocol-circuits/src/index.ts index 97671183a7c6..3eb5fe1bf86e 100644 --- a/yarn-project/noir-protocol-circuits/src/index.ts +++ b/yarn-project/noir-protocol-circuits/src/index.ts @@ -229,6 +229,7 @@ export async function executeBaseRollup(baseRollupInputs: BaseRollupInputs): Pro * */ async function executePrivateKernelInitWithACVM(input: InitInputType): Promise { + console.log(JSON.stringify(input, null, 4)); const initialWitnessMap = abiEncode(PrivateKernelInitSimulatedJson.abi as Abi, input as any); // Execute the circuit on those initial witness values diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index addce00443b5..31560c108d61 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -1116,8 +1116,12 @@ export function mapBaseOrMergeRollupPublicInputsToNoir( baseOrMergeRollupPublicInputs.startContractTreeSnapshot, ), end_contract_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(baseOrMergeRollupPublicInputs.endContractTreeSnapshot), - start_public_data_tree_root: mapFieldToNoir(baseOrMergeRollupPublicInputs.startPublicDataTreeRoot), - end_public_data_tree_root: mapFieldToNoir(baseOrMergeRollupPublicInputs.endPublicDataTreeRoot), + start_public_data_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir( + baseOrMergeRollupPublicInputs.startPublicDataTreeSnapshot, + ), + end_public_data_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir( + baseOrMergeRollupPublicInputs.endPublicDataTreeSnapshot, + ), calldata_hash: mapTuple(baseOrMergeRollupPublicInputs.calldataHash, mapFieldToNoir), }; } @@ -1169,8 +1173,8 @@ export function mapBaseOrMergeRollupPublicInputsFromNoir( mapAppendOnlyTreeSnapshotFromNoir(baseOrMergeRollupPublicInputs.end_nullifier_tree_snapshot), mapAppendOnlyTreeSnapshotFromNoir(baseOrMergeRollupPublicInputs.start_contract_tree_snapshot), mapAppendOnlyTreeSnapshotFromNoir(baseOrMergeRollupPublicInputs.end_contract_tree_snapshot), - mapFieldFromNoir(baseOrMergeRollupPublicInputs.start_public_data_tree_root), - mapFieldFromNoir(baseOrMergeRollupPublicInputs.end_public_data_tree_root), + mapAppendOnlyTreeSnapshotFromNoir(baseOrMergeRollupPublicInputs.start_public_data_tree_snapshot), + mapAppendOnlyTreeSnapshotFromNoir(baseOrMergeRollupPublicInputs.end_public_data_tree_snapshot), mapTupleFromNoir(baseOrMergeRollupPublicInputs.calldata_hash, 2, mapFieldFromNoir), ); } @@ -1268,8 +1272,8 @@ export function mapRootRollupPublicInputsFromNoir( mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.end_nullifier_tree_snapshot), mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.start_contract_tree_snapshot), mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.end_contract_tree_snapshot), - mapFieldFromNoir(rootRollupPublicInputs.start_public_data_tree_root), - mapFieldFromNoir(rootRollupPublicInputs.end_public_data_tree_root), + mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.start_public_data_tree_snapshot), + mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.end_public_data_tree_snapshot), mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.start_tree_of_historical_note_hash_tree_roots_snapshot), mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.end_tree_of_historical_note_hash_tree_roots_snapshot), mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.start_tree_of_historical_contract_tree_roots_snapshot), @@ -1388,7 +1392,7 @@ export function mapBaseRollupInputsToNoir(inputs: BaseRollupInputs): BaseRollupI start_note_hash_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startNoteHashTreeSnapshot), start_nullifier_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startNullifierTreeSnapshot), start_contract_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startContractTreeSnapshot), - start_public_data_tree_root: mapFieldToNoir(inputs.startPublicDataTreeRoot), + start_public_data_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startPublicDataTreeSnapshot), start_blocks_tree_snapshot: mapAppendOnlyTreeSnapshotToNoir(inputs.startBlocksTreeSnapshot), sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapFieldToNoir), sorted_new_nullifiers_indexes: mapTuple(inputs.sortednewNullifiersIndexes, (index: number) => diff --git a/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts b/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts index 2d7c5fe1427a..12da7b33c910 100644 --- a/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/rollup_base_types.ts @@ -206,7 +206,7 @@ export interface BaseRollupInputs { start_note_hash_tree_snapshot: AppendOnlyTreeSnapshot; start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot; start_contract_tree_snapshot: AppendOnlyTreeSnapshot; - start_public_data_tree_root: Field; + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot; start_blocks_tree_snapshot: AppendOnlyTreeSnapshot; sorted_new_nullifiers: FixedLengthArray; sorted_new_nullifiers_indexes: FixedLengthArray; @@ -237,8 +237,8 @@ export interface BaseOrMergeRollupPublicInputs { end_nullifier_tree_snapshot: AppendOnlyTreeSnapshot; start_contract_tree_snapshot: AppendOnlyTreeSnapshot; end_contract_tree_snapshot: AppendOnlyTreeSnapshot; - start_public_data_tree_root: Field; - end_public_data_tree_root: Field; + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot; + end_public_data_tree_snapshot: AppendOnlyTreeSnapshot; calldata_hash: FixedLengthArray; } diff --git a/yarn-project/noir-protocol-circuits/src/types/rollup_merge_types.ts b/yarn-project/noir-protocol-circuits/src/types/rollup_merge_types.ts index 233624ab34de..8cb53d235b45 100644 --- a/yarn-project/noir-protocol-circuits/src/types/rollup_merge_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/rollup_merge_types.ts @@ -41,8 +41,8 @@ export interface BaseOrMergeRollupPublicInputs { end_nullifier_tree_snapshot: AppendOnlyTreeSnapshot; start_contract_tree_snapshot: AppendOnlyTreeSnapshot; end_contract_tree_snapshot: AppendOnlyTreeSnapshot; - start_public_data_tree_root: Field; - end_public_data_tree_root: Field; + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot; + end_public_data_tree_snapshot: AppendOnlyTreeSnapshot; calldata_hash: FixedLengthArray; } diff --git a/yarn-project/noir-protocol-circuits/src/types/rollup_root_types.ts b/yarn-project/noir-protocol-circuits/src/types/rollup_root_types.ts index 63c4b01df512..9dfe24813147 100644 --- a/yarn-project/noir-protocol-circuits/src/types/rollup_root_types.ts +++ b/yarn-project/noir-protocol-circuits/src/types/rollup_root_types.ts @@ -41,8 +41,8 @@ export interface BaseOrMergeRollupPublicInputs { end_nullifier_tree_snapshot: AppendOnlyTreeSnapshot; start_contract_tree_snapshot: AppendOnlyTreeSnapshot; end_contract_tree_snapshot: AppendOnlyTreeSnapshot; - start_public_data_tree_root: Field; - end_public_data_tree_root: Field; + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot; + end_public_data_tree_snapshot: AppendOnlyTreeSnapshot; calldata_hash: FixedLengthArray; } @@ -81,8 +81,8 @@ export interface RootRollupPublicInputs { end_nullifier_tree_snapshot: AppendOnlyTreeSnapshot; start_contract_tree_snapshot: AppendOnlyTreeSnapshot; end_contract_tree_snapshot: AppendOnlyTreeSnapshot; - start_public_data_tree_root: Field; - end_public_data_tree_root: Field; + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot; + end_public_data_tree_snapshot: AppendOnlyTreeSnapshot; start_tree_of_historical_note_hash_tree_roots_snapshot: AppendOnlyTreeSnapshot; end_tree_of_historical_note_hash_tree_roots_snapshot: AppendOnlyTreeSnapshot; start_tree_of_historical_contract_tree_roots_snapshot: AppendOnlyTreeSnapshot; diff --git a/yarn-project/pxe/src/synchronizer/synchronizer.ts b/yarn-project/pxe/src/synchronizer/synchronizer.ts index 98dfd30c7e63..c8bfc395935f 100644 --- a/yarn-project/pxe/src/synchronizer/synchronizer.ts +++ b/yarn-project/pxe/src/synchronizer/synchronizer.ts @@ -208,7 +208,7 @@ export class Synchronizer { block.endL1ToL2MessagesTreeSnapshot.root, block.endBlocksTreeSnapshot.root, Fr.ZERO, // todo: private kernel vk tree root - block.endPublicDataTreeRoot, + block.endPublicDataTreeSnapshot.root, globalsHash, ); diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index c90467bcf115..ba7462111938 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -150,7 +150,7 @@ describe('sequencer/solo_block_builder', () => { rootRollupOutput.endNullifierTreeSnapshot.root, rootRollupOutput.endContractTreeSnapshot.root, rootRollupOutput.endL1ToL2MessagesTreeSnapshot.root, - rootRollupOutput.endPublicDataTreeRoot, + rootRollupOutput.endPublicDataTreeSnapshot.root, ); await expectsDb.appendLeaves(MerkleTreeId.BLOCKS_TREE, [blockHash.toBuffer()]); }; @@ -183,14 +183,14 @@ describe('sequencer/solo_block_builder', () => { baseRollupOutputLeft.endContractTreeSnapshot = await getTreeSnapshot(MerkleTreeId.CONTRACT_TREE); baseRollupOutputLeft.endNullifierTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE); baseRollupOutputLeft.endNoteHashTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE); - baseRollupOutputLeft.endPublicDataTreeRoot = (await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE)).root; + baseRollupOutputLeft.endPublicDataTreeSnapshot = await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); // Same for the two txs on the right await updateExpectedTreesFromTxs(txsRight); baseRollupOutputRight.endContractTreeSnapshot = await getTreeSnapshot(MerkleTreeId.CONTRACT_TREE); baseRollupOutputRight.endNullifierTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE); baseRollupOutputRight.endNoteHashTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE); - baseRollupOutputRight.endPublicDataTreeRoot = (await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE)).root; + 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 @@ -198,7 +198,7 @@ describe('sequencer/solo_block_builder', () => { rootRollupOutput.endContractTreeSnapshot = await getTreeSnapshot(MerkleTreeId.CONTRACT_TREE); rootRollupOutput.endNullifierTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE); rootRollupOutput.endNoteHashTreeSnapshot = await getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE); - rootRollupOutput.endPublicDataTreeRoot = (await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE)).root; + rootRollupOutput.endPublicDataTreeSnapshot = await getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); rootRollupOutput.endL1ToL2MessagesTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE); @@ -231,8 +231,8 @@ describe('sequencer/solo_block_builder', () => { endNullifierTreeSnapshot: rootRollupOutput.endNullifierTreeSnapshot, startContractTreeSnapshot: rootRollupOutput.startContractTreeSnapshot, endContractTreeSnapshot: rootRollupOutput.endContractTreeSnapshot, - startPublicDataTreeRoot: rootRollupOutput.startPublicDataTreeRoot, - endPublicDataTreeRoot: rootRollupOutput.endPublicDataTreeRoot, + startPublicDataTreeSnapshot: rootRollupOutput.startPublicDataTreeSnapshot, + endPublicDataTreeSnapshot: rootRollupOutput.endPublicDataTreeSnapshot, startL1ToL2MessagesTreeSnapshot: rootRollupOutput.startL1ToL2MessagesTreeSnapshot, endL1ToL2MessagesTreeSnapshot: rootRollupOutput.endL1ToL2MessagesTreeSnapshot, startBlocksTreeSnapshot: rootRollupOutput.startBlocksTreeSnapshot, diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index 0cb7bcda8252..f8eb217db35a 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -125,7 +125,7 @@ export class SoloBlockBuilder implements BlockBuilder { endNoteHashTreeSnapshot, endNullifierTreeSnapshot, endContractTreeSnapshot, - endPublicDataTreeRoot, + endPublicDataTreeSnapshot, endL1ToL2MessagesTreeSnapshot, endBlocksTreeSnapshot, } = circuitsOutput; @@ -163,8 +163,8 @@ export class SoloBlockBuilder implements BlockBuilder { endNullifierTreeSnapshot, startContractTreeSnapshot, endContractTreeSnapshot, - startPublicDataTreeRoot: startPublicDataTreeSnapshot.root, - endPublicDataTreeRoot, + startPublicDataTreeSnapshot, + endPublicDataTreeSnapshot, startL1ToL2MessagesTreeSnapshot: startL1ToL2MessageTreeSnapshot, endL1ToL2MessagesTreeSnapshot, startBlocksTreeSnapshot, @@ -388,7 +388,7 @@ export class SoloBlockBuilder implements BlockBuilder { */ protected async validatePublicDataTreeRoot(output: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs) { const localTree = await this.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); - const simulatedTreeRoot = output[`endPublicDataTreeRoot`]; + const simulatedTreeRoot = output[`endPublicDataTreeSnapshot`]; if (!simulatedTreeRoot.toBuffer().equals(localTree.root.toBuffer())) { throw new Error(`PublicData tree root mismatch (local ${localTree.root}, simulated ${simulatedTreeRoot})`); @@ -764,7 +764,7 @@ export class SoloBlockBuilder implements BlockBuilder { startNullifierTreeSnapshot, startContractTreeSnapshot, startNoteHashTreeSnapshot, - startPublicDataTreeRoot: startPublicDataTreeSnapshot.root, + startPublicDataTreeSnapshot, startBlocksTreeSnapshot, sortedPublicDataWrites: [ leftPublicDataUpdateRequestInfo.sortedPublicDataWrites, diff --git a/yarn-project/types/src/l2_block.ts b/yarn-project/types/src/l2_block.ts index 67832ef2da73..ee50fbebd707 100644 --- a/yarn-project/types/src/l2_block.ts +++ b/yarn-project/types/src/l2_block.ts @@ -78,9 +78,9 @@ export class L2Block { */ public startContractTreeSnapshot: AppendOnlyTreeSnapshot, /** - * The tree root of the public data tree at the start of the rollup. + * The tree snapshot of the public data tree at the start of the rollup. */ - public startPublicDataTreeRoot: Fr, + public startPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** * The tree snapshot of the L2 message tree at the start of the rollup. */ @@ -102,9 +102,9 @@ export class L2Block { */ public endContractTreeSnapshot: AppendOnlyTreeSnapshot, /** - * The tree root of the public data tree at the end of the rollup. + * The tree snapshot of the public data tree at the end of the rollup. */ - public endPublicDataTreeRoot: Fr, + public endPublicDataTreeSnapshot: AppendOnlyTreeSnapshot, /** * The tree snapshot of the L2 message tree at the end of the rollup. */ @@ -214,13 +214,13 @@ export class L2Block { startNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(0), startNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(0), startContractTreeSnapshot: makeAppendOnlyTreeSnapshot(0), - startPublicDataTreeRoot: Fr.random(), + startPublicDataTreeSnapshot: makeAppendOnlyTreeSnapshot(0), startL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(0), startBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(0), endNoteHashTreeSnapshot: makeAppendOnlyTreeSnapshot(newCommitments.length), endNullifierTreeSnapshot: makeAppendOnlyTreeSnapshot(newNullifiers.length), endContractTreeSnapshot: makeAppendOnlyTreeSnapshot(newContracts.length), - endPublicDataTreeRoot: Fr.random(), + endPublicDataTreeSnapshot: makeAppendOnlyTreeSnapshot(0), endL1ToL2MessagesTreeSnapshot: makeAppendOnlyTreeSnapshot(1), endBlocksTreeSnapshot: makeAppendOnlyTreeSnapshot(1), newCommitments, @@ -269,9 +269,9 @@ export class L2Block { */ startContractTreeSnapshot: AppendOnlyTreeSnapshot; /** - * The tree root of the public data tree at the start of the rollup. + * The tree snapshot of the public data tree at the start of the rollup. */ - startPublicDataTreeRoot: Fr; + startPublicDataTreeSnapshot: AppendOnlyTreeSnapshot; /** * The tree snapshot of the L2 message tree at the start of the rollup. */ @@ -293,9 +293,9 @@ export class L2Block { */ endContractTreeSnapshot: AppendOnlyTreeSnapshot; /** - * The tree root of the public data tree at the end of the rollup. + * The tree snapshot of the public data tree at the end of the rollup. */ - endPublicDataTreeRoot: Fr; + endPublicDataTreeSnapshot: AppendOnlyTreeSnapshot; /** * The tree snapshot of the L2 message tree at the end of the rollup. */ @@ -350,13 +350,13 @@ export class L2Block { fields.startNoteHashTreeSnapshot, fields.startNullifierTreeSnapshot, fields.startContractTreeSnapshot, - fields.startPublicDataTreeRoot, + fields.startPublicDataTreeSnapshot, fields.startL1ToL2MessagesTreeSnapshot, fields.startBlocksTreeSnapshot, fields.endNoteHashTreeSnapshot, fields.endNullifierTreeSnapshot, fields.endContractTreeSnapshot, - fields.endPublicDataTreeRoot, + fields.endPublicDataTreeSnapshot, fields.endL1ToL2MessagesTreeSnapshot, fields.endBlocksTreeSnapshot, fields.newCommitments, @@ -385,13 +385,13 @@ export class L2Block { this.startNoteHashTreeSnapshot, this.startNullifierTreeSnapshot, this.startContractTreeSnapshot, - this.startPublicDataTreeRoot, + this.startPublicDataTreeSnapshot, this.startL1ToL2MessagesTreeSnapshot, this.startBlocksTreeSnapshot, this.endNoteHashTreeSnapshot, this.endNullifierTreeSnapshot, this.endContractTreeSnapshot, - this.endPublicDataTreeRoot, + this.endPublicDataTreeSnapshot, this.endL1ToL2MessagesTreeSnapshot, this.endBlocksTreeSnapshot, this.newCommitments.length, @@ -447,13 +447,13 @@ export class L2Block { const startNoteHashTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const startNullifierTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const startContractTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); - const startPublicDataTreeRoot = reader.readObject(Fr); + const startPublicDataTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const startL1ToL2MessagesTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const startBlocksTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const endNoteHashTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const endNullifierTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const endContractTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); - const endPublicDataTreeRoot = reader.readObject(Fr); + const endPublicDataTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const endL1ToL2MessagesTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const endBlocksTreeSnapshot = reader.readObject(AppendOnlyTreeSnapshot); const newCommitments = reader.readVector(Fr); @@ -471,13 +471,13 @@ export class L2Block { startNoteHashTreeSnapshot, startNullifierTreeSnapshot, startContractTreeSnapshot, - startPublicDataTreeRoot, + startPublicDataTreeSnapshot, startL1ToL2MessagesTreeSnapshot: startL1ToL2MessagesTreeSnapshot, startBlocksTreeSnapshot, endNoteHashTreeSnapshot, endNullifierTreeSnapshot, endContractTreeSnapshot, - endPublicDataTreeRoot, + endPublicDataTreeSnapshot, endL1ToL2MessagesTreeSnapshot, endBlocksTreeSnapshot, newCommitments, @@ -587,13 +587,13 @@ export class L2Block { this.startNoteHashTreeSnapshot, this.startNullifierTreeSnapshot, this.startContractTreeSnapshot, - this.startPublicDataTreeRoot, + this.startPublicDataTreeSnapshot, this.startL1ToL2MessagesTreeSnapshot, this.startBlocksTreeSnapshot, this.endNoteHashTreeSnapshot, this.endNullifierTreeSnapshot, this.endContractTreeSnapshot, - this.endPublicDataTreeRoot, + this.endPublicDataTreeSnapshot, this.endL1ToL2MessagesTreeSnapshot, this.endBlocksTreeSnapshot, this.getCalldataHash(), @@ -613,7 +613,7 @@ export class L2Block { this.startNoteHashTreeSnapshot, this.startNullifierTreeSnapshot, this.startContractTreeSnapshot, - this.startPublicDataTreeRoot, + this.startPublicDataTreeSnapshot, this.startL1ToL2MessagesTreeSnapshot, this.startBlocksTreeSnapshot, ); @@ -630,7 +630,7 @@ export class L2Block { this.endNoteHashTreeSnapshot, this.endNullifierTreeSnapshot, this.endContractTreeSnapshot, - this.endPublicDataTreeRoot, + this.endPublicDataTreeSnapshot, this.endL1ToL2MessagesTreeSnapshot, this.endBlocksTreeSnapshot, ); @@ -841,14 +841,14 @@ export class L2Block { `startNoteHashTreeSnapshot: ${inspectTreeSnapshot(this.startNoteHashTreeSnapshot)}`, `startNullifierTreeSnapshot: ${inspectTreeSnapshot(this.startNullifierTreeSnapshot)}`, `startContractTreeSnapshot: ${inspectTreeSnapshot(this.startContractTreeSnapshot)}`, - `startPublicDataTreeRoot: ${this.startPublicDataTreeRoot.toString()}`, + `startPublicDataTreeSnapshot: ${this.startPublicDataTreeSnapshot.toString()}`, `startL1ToL2MessagesTreeSnapshot: ${inspectTreeSnapshot(this.startL1ToL2MessagesTreeSnapshot)}`, `startBlocksTreeSnapshot: ${inspectTreeSnapshot(this.startBlocksTreeSnapshot)}`, `endNoteHashTreeSnapshot: ${inspectTreeSnapshot(this.endNoteHashTreeSnapshot)}`, `endNullifierTreeSnapshot: ${inspectTreeSnapshot(this.endNullifierTreeSnapshot)}`, `endContractTreeSnapshot: ${inspectTreeSnapshot(this.endContractTreeSnapshot)}`, - `endPublicDataTreeRoot: ${this.endPublicDataTreeRoot.toString()}`, - `endPublicDataTreeRoot: ${this.endPublicDataTreeRoot.toString()}`, + `endPublicDataTreeSnapshot: ${this.endPublicDataTreeSnapshot.toString()}`, + `endPublicDataTreeSnapshot: ${this.endPublicDataTreeSnapshot.toString()}`, `endL1ToL2MessagesTreeSnapshot: ${inspectTreeSnapshot(this.endL1ToL2MessagesTreeSnapshot)}`, `endBlocksTreeSnapshot: ${inspectTreeSnapshot(this.endBlocksTreeSnapshot)}`, `newCommitments: ${inspectFrArray(this.newCommitments)}`, diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts index d5e3798a7c60..a75c09b9a3fb 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts @@ -70,13 +70,13 @@ const getMockBlock = (blockNumber: number, newContractsCommitments?: Buffer[]) = startNoteHashTreeSnapshot: getMockTreeSnapshot(), startNullifierTreeSnapshot: getMockTreeSnapshot(), startContractTreeSnapshot: getMockTreeSnapshot(), - startPublicDataTreeRoot: Fr.random(), + startPublicDataTreeSnapshot: getMockTreeSnapshot(), startL1ToL2MessagesTreeSnapshot: getMockTreeSnapshot(), startBlocksTreeSnapshot: getMockTreeSnapshot(), endNoteHashTreeSnapshot: getMockTreeSnapshot(), endNullifierTreeSnapshot: getMockTreeSnapshot(), endContractTreeSnapshot: getMockTreeSnapshot(), - endPublicDataTreeRoot: Fr.random(), + endPublicDataTreeSnapshot: getMockTreeSnapshot(), endL1ToL2MessagesTreeSnapshot: getMockTreeSnapshot(), endBlocksTreeSnapshot: getMockTreeSnapshot(), newCommitments: times(MAX_NEW_COMMITMENTS_PER_TX, Fr.random), diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 925e55e0ef9c..e889e7282732 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -539,7 +539,7 @@ export class MerkleTrees implements MerkleTreeDb { [l2Block.endContractTreeSnapshot.root, MerkleTreeId.CONTRACT_TREE], [l2Block.endNullifierTreeSnapshot.root, MerkleTreeId.NULLIFIER_TREE], [l2Block.endNoteHashTreeSnapshot.root, MerkleTreeId.NOTE_HASH_TREE], - [l2Block.endPublicDataTreeRoot, MerkleTreeId.PUBLIC_DATA_TREE], + [l2Block.endPublicDataTreeSnapshot.root, MerkleTreeId.PUBLIC_DATA_TREE], [l2Block.endL1ToL2MessagesTreeSnapshot.root, MerkleTreeId.L1_TO_L2_MESSAGES_TREE], [l2Block.endBlocksTreeSnapshot.root, MerkleTreeId.BLOCKS_TREE], ] as const; From edb4548ba3b25fc83201c25d9436062c839a1c58 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 09:38:11 +0000 Subject: [PATCH 18/40] fix: addressed some PR comments --- yarn-project/merkle-tree/src/index.ts | 2 +- yarn-project/merkle-tree/src/new_tree.ts | 2 +- .../src/snapshots/indexed_tree_snapshot.ts | 2 +- .../src/sparse_tree/sparse_tree.test.ts | 6 +-- .../standard_indexed_tree.ts | 42 ++++++++++--------- .../src/standard_tree/standard_tree.test.ts | 6 +-- 6 files changed, 32 insertions(+), 28 deletions(-) diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index c77fc1c9decc..10ac123395a0 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -7,7 +7,7 @@ export * from './sparse_tree/sparse_tree.js'; export { StandardIndexedTree } from './standard_indexed_tree/standard_indexed_tree.js'; export * from './standard_tree/standard_tree.js'; export { INITIAL_LEAF } from './tree_base.js'; -export { newTree, builder as treeBuilder } from './new_tree.js'; +export { newTree, treeBuilder } from './new_tree.js'; export { loadTree } from './load_tree.js'; export * from './snapshots/snapshot_builder.js'; export * from './snapshots/full_snapshot.js'; diff --git a/yarn-project/merkle-tree/src/new_tree.ts b/yarn-project/merkle-tree/src/new_tree.ts index 819c0409c6c9..31bfdefd9d8d 100644 --- a/yarn-project/merkle-tree/src/new_tree.ts +++ b/yarn-project/merkle-tree/src/new_tree.ts @@ -9,7 +9,7 @@ import { TreeBase } from './tree_base.js'; * @param clazz - The class to be instantiated. * @returns A builder function. */ -export function builder( +export function treeBuilder( clazz: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => T, ) { return (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => { diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts index 98e5373537c2..28aeefdc9532 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts @@ -101,7 +101,7 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre async findLeafIndex(value: Buffer): Promise { const index = await this.tree.findLeafIndex(value, false); - if (index && index < this.getNumLeaves()) { + if (index !== undefined && index < this.getNumLeaves()) { return index; } } diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts index 7f1df12cc0f1..3e23772fb2e3 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts @@ -7,7 +7,7 @@ import { default as levelup } from 'levelup'; import { INITIAL_LEAF, newTree } from '../index.js'; import { UpdateOnlyTree } from '../interfaces/update_only_tree.js'; import { loadTree } from '../load_tree.js'; -import { builder } from '../new_tree.js'; +import { treeBuilder } from '../new_tree.js'; import { Pedersen } from '../pedersen.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; @@ -22,11 +22,11 @@ const createDb = async ( name: string, depth: number, ): Promise => { - return await newTree(builder(SparseTree), levelUp, hasher, name, depth); + return await newTree(treeBuilder(SparseTree), levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string): Promise => { - return await loadTree(builder(SparseTree), levelUp, hasher, name); + return await loadTree(treeBuilder(SparseTree), levelUp, hasher, name); }; const TEST_TREE_DEPTH = 3; diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index a935bcef75ef..325b438a0f14 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -67,6 +67,22 @@ export const buildDbKeyForLeafIndex = (name: string, key: bigint) => { return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}`; }; +/** + * Pre-compute empty witness. + * @param treeHeight - Height of tree for sibling path. + * @returns An empty witness. + */ +function getEmptyLowLeafWitness( + treeHeight: N, + leafPreimageFactory: PreimageFactory, +): LowLeafWitnessData { + return { + leafPreimage: leafPreimageFactory.empty(), + index: 0n, + siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), + }; +} + /** * Standard implementation of an indexed tree. */ @@ -153,11 +169,12 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { if (includeUncommitted) { const cachedLowLeafIndex = this.getCachedLowLeafIndex(newKey); - const cachedLowLeafPreimage = - cachedLowLeafIndex !== undefined ? this.getCachedPreimage(cachedLowLeafIndex) : undefined; - if (cachedLowLeafIndex && (!lowLeafIndex || cachedLowLeafPreimage!.getKey() > lowLeafPreimage!.getKey())) { - lowLeafIndex = cachedLowLeafIndex; - lowLeafPreimage = cachedLowLeafPreimage; + if (cachedLowLeafIndex !== undefined) { + const cachedLowLeafPreimage = this.getCachedPreimage(cachedLowLeafIndex)!; + if (!lowLeafPreimage || cachedLowLeafPreimage.getKey() > lowLeafPreimage.getKey()) { + lowLeafIndex = cachedLowLeafIndex; + lowLeafPreimage = cachedLowLeafPreimage; + } } } @@ -340,19 +357,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { } } - /** - * Pre-compute empty witness. - * @param treeHeight - Height of tree for sibling path. - * @returns An empty witness. - */ - getEmptyLowLeafWitness(treeHeight: N): LowLeafWitnessData { - return { - leafPreimage: this.leafPreimageFactory.empty(), - index: 0n, - siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))), - }; - } - /* eslint-disable jsdoc/require-description-complete-sentence */ /* The following doc block messes up with complete-sentence, so we just disable it */ @@ -469,7 +473,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { leaves: Buffer[], subtreeHeight: SubtreeHeight, ): Promise> { - const emptyLowLeafWitness = this.getEmptyLowLeafWitness(this.getDepth() as TreeHeight); + const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); // Accumulators const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); const pendingInsertionSubtree: IndexedTreeLeafPreimage[] = leaves.map(() => this.leafPreimageFactory.empty()); diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts index db67453c4bc8..5849c4e58068 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts @@ -4,7 +4,7 @@ import { Hasher } from '@aztec/types'; import { default as levelup } from 'levelup'; import { loadTree } from '../load_tree.js'; -import { builder, newTree } from '../new_tree.js'; +import { newTree, treeBuilder } from '../new_tree.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; import { createMemDown } from '../test/utils/create_mem_down.js'; @@ -13,11 +13,11 @@ import { INITIAL_LEAF } from '../tree_base.js'; import { StandardTree } from './standard_tree.js'; const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => { - return await newTree(builder(StandardTree), levelUp, hasher, name, depth); + return await newTree(treeBuilder(StandardTree), levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(builder(StandardTree), levelUp, hasher, name); + return await loadTree(treeBuilder(StandardTree), levelUp, hasher, name); }; treeTestSuite('StandardTree', createDb, createFromName); From e731f97ae6749bd5bba32585dfdb67bcc034c746 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 10:10:04 +0000 Subject: [PATCH 19/40] fix: avoid findLeafIndex in sparse trees --- .../merkle-tree/src/sparse_tree/sparse_tree.ts | 4 ++++ .../test/standard_indexed_tree.test.ts | 16 ++++++++++++++++ .../src/standard_tree/standard_tree.test.ts | 16 ++++++++++++++++ .../src/standard_tree/standard_tree.ts | 10 ++++++++++ yarn-project/merkle-tree/src/test/test_suite.ts | 14 -------------- yarn-project/merkle-tree/src/tree_base.ts | 10 +--------- 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts index 92cdc4152fc1..4c85461987ea 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts @@ -41,4 +41,8 @@ export class SparseTree extends TreeBase implements UpdateOnlyTree { public getSnapshot(block: number): Promise { return this.#snapshotBuilder.getSnapshot(block); } + + public findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + throw new Error('Finding leaf index is not supported for sparse trees'); + } } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index 015193954ebe..9bcef10bf789 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -495,4 +495,20 @@ describe('StandardIndexedTreeSpecific', () => { const actualRoot = insertTree.getRoot(true); expect(actualRoot).toEqual(expectedRoot); }); + + it('should be able to find indexes of leaves', async () => { + const db = levelup(createMemDown()); + const tree = await createDb(db, pedersen, 'test', 3); + const values = [Buffer.alloc(32, 1), Buffer.alloc(32, 2)]; + + await tree.appendLeaves([values[0]]); + + expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); + expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); + expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); + + await tree.commit(); + + expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); + }); }); diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts index 5849c4e58068..d2d2d0396c40 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts @@ -69,4 +69,20 @@ describe('StandardTree_batchAppend', () => { expect(tree.getRoot(true)).toEqual(root); }); + + it('should be able to find indexes of leaves', async () => { + const db = levelup(createMemDown()); + const tree = await createDb(db, pedersen, 'test', 3); + const values = [Buffer.alloc(32, 1), Buffer.alloc(32, 2)]; + + await tree.appendLeaves([values[0]]); + + expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); + expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); + expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); + + await tree.commit(); + + expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); + }); }); diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts index 0b92572a4b82..55b4f5324690 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts @@ -24,4 +24,14 @@ export class StandardTree extends TreeBase implements AppendOnlyTree { public getSnapshot(block: number): Promise { return this.#snapshotBuilder.getSnapshot(block); } + + public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + for (let i = 0n; i < this.getNumLeaves(includeUncommitted); i++) { + const currentValue = await this.getLeafValue(i, includeUncommitted); + if (currentValue && currentValue.equals(value)) { + return i; + } + } + return undefined; + } } diff --git a/yarn-project/merkle-tree/src/test/test_suite.ts b/yarn-project/merkle-tree/src/test/test_suite.ts index 51886a090b3d..1940f32ce913 100644 --- a/yarn-project/merkle-tree/src/test/test_suite.ts +++ b/yarn-project/merkle-tree/src/test/test_suite.ts @@ -157,19 +157,5 @@ export const treeTestSuite = ( expect(deserialized2.elem).toEqual(siblingPath); expect(deserialized2.adv).toBe(4 + 10 * 32); }); - - it('should be able to find indexes of leaves', async () => { - const db = levelup(createMemDown()); - const tree = await createDb(db, pedersen, 'test', 10); - await appendLeaves(tree, values.slice(0, 1)); - - expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); - expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); - expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); - - await tree.commit(); - - expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); - }); }); }; diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index f9f58068cbe2..291ac258082b 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -307,13 +307,5 @@ export abstract class TreeBase implements MerkleTree { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). */ - public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { - for (let i = 0n; i < this.getNumLeaves(includeUncommitted); i++) { - const currentValue = await this.getLeafValue(i, includeUncommitted); - if (currentValue && currentValue.equals(value)) { - return i; - } - } - return undefined; - } + abstract findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise; } From 037a47961bfd2993758f03f3e5de2796cfa7ccbe Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 10:37:17 +0000 Subject: [PATCH 20/40] refactor: avoid createTree helper --- .../src/client/private_execution.test.ts | 4 +-- .../src/e2e_blacklist_token_contract.test.ts | 10 ++---- .../end-to-end/src/e2e_slow_tree.test.ts | 10 ++---- yarn-project/merkle-tree/src/index.ts | 2 +- yarn-project/merkle-tree/src/load_tree.ts | 4 +-- yarn-project/merkle-tree/src/new_tree.ts | 17 ++------- .../snapshots/append_only_snapshot.test.ts | 4 +-- .../src/snapshots/full_snapshot.test.ts | 4 +-- .../snapshots/indexed_tree_snapshot.test.ts | 16 ++++----- .../src/sparse_tree/sparse_tree.test.ts | 5 ++- .../test/standard_indexed_tree.test.ts | 35 +++++-------------- .../src/standard_tree/standard_tree.test.ts | 6 ++-- .../src/world-state-db/merkle_trees.ts | 21 ++++++----- 13 files changed, 47 insertions(+), 91 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index c35c9cf0ae0f..6b0a7816a51f 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -27,7 +27,7 @@ import { pedersenHash } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { AppendOnlyTree, Pedersen, StandardTree, newTree, treeBuilder } from '@aztec/merkle-tree'; +import { AppendOnlyTree, Pedersen, StandardTree, newTree } from '@aztec/merkle-tree'; import { ChildContractArtifact, ImportTestContractArtifact, @@ -126,7 +126,7 @@ describe('Private Execution test suite', () => { if (!trees[name]) { const db = levelup(createMemDown()); const pedersen = new Pedersen(); - trees[name] = await newTree(treeBuilder(StandardTree), db, pedersen, name, treeHeights[name]); + trees[name] = await newTree(StandardTree, db, pedersen, name, treeHeights[name]); } await trees[name].appendLeaves(leaves.map(l => l.toBuffer())); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts index 6a2232eee479..1c1825c7d837 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts @@ -14,7 +14,7 @@ import { computeAuthWitMessageHash, computeMessageSecretHash, } from '@aztec/aztec.js'; -import { Pedersen, SparseTree, newTree, treeBuilder } from '@aztec/merkle-tree'; +import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; import { SlowTreeContract, TokenBlacklistContract, TokenContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; @@ -107,13 +107,7 @@ describe('e2e_blacklist_token_contract', () => { slowTree = await SlowTreeContract.deploy(wallets[0]).send().deployed(); const depth = 254; - slowUpdateTreeSimulator = await newTree( - treeBuilder(SparseTree), - levelup(createMemDown()), - new Pedersen(), - 'test', - depth, - ); + slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); const deployTx = TokenBlacklistContract.deploy(wallets[0], accounts[0], slowTree.address).send({}); const receipt = await deployTx.wait(); diff --git a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts index e0d58be36ee7..87c267e5e823 100644 --- a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts +++ b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts @@ -1,6 +1,6 @@ /* eslint-disable camelcase */ import { CheatCodes, DebugLogger, Fr, Wallet } from '@aztec/aztec.js'; -import { Pedersen, SparseTree, newTree, treeBuilder } from '@aztec/merkle-tree'; +import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; import { SlowTreeContract } from '@aztec/noir-contracts/types'; import { default as levelup } from 'levelup'; @@ -27,13 +27,7 @@ describe('e2e_slow_tree', () => { it('Messing around with noir slow tree', async () => { const depth = 254; - const slowUpdateTreeSimulator = await newTree( - treeBuilder(SparseTree), - levelup(createMemDown()), - new Pedersen(), - 'test', - depth, - ); + const slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); const getMembershipProof = async (index: bigint, includeUncommitted: boolean) => { return { index, diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index 10ac123395a0..68826f44e42c 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -7,7 +7,7 @@ export * from './sparse_tree/sparse_tree.js'; export { StandardIndexedTree } from './standard_indexed_tree/standard_indexed_tree.js'; export * from './standard_tree/standard_tree.js'; export { INITIAL_LEAF } from './tree_base.js'; -export { newTree, treeBuilder } from './new_tree.js'; +export { newTree } from './new_tree.js'; export { loadTree } from './load_tree.js'; export * from './snapshots/snapshot_builder.js'; export * from './snapshots/full_snapshot.js'; diff --git a/yarn-project/merkle-tree/src/load_tree.ts b/yarn-project/merkle-tree/src/load_tree.ts index bd852b930fd9..9753a2d528d5 100644 --- a/yarn-project/merkle-tree/src/load_tree.ts +++ b/yarn-project/merkle-tree/src/load_tree.ts @@ -13,7 +13,7 @@ import { TreeBase, decodeMeta } from './tree_base.js'; * @returns The newly created tree. */ export async function loadTree( - c: (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => T, + c: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => T, db: LevelUp, hasher: Hasher, name: string, @@ -21,6 +21,6 @@ export async function loadTree( const meta: Buffer = await db.get(name); const { root, depth, size } = decodeMeta(meta); - const tree = c(db, hasher, name, depth, size, root); + const tree = new c(db, hasher, name, depth, size, root); return tree; } diff --git a/yarn-project/merkle-tree/src/new_tree.ts b/yarn-project/merkle-tree/src/new_tree.ts index 31bfdefd9d8d..1395d012d254 100644 --- a/yarn-project/merkle-tree/src/new_tree.ts +++ b/yarn-project/merkle-tree/src/new_tree.ts @@ -4,19 +4,6 @@ import { LevelUp } from 'levelup'; import { TreeBase } from './tree_base.js'; -/** - * Wraps a constructor in a regular builder - * @param clazz - The class to be instantiated. - * @returns A builder function. - */ -export function treeBuilder( - clazz: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => T, -) { - return (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root?: Buffer) => { - return new clazz(db, hasher, name, depth, size, root); - }; -} - /** * Creates a new tree. * @param c - The class of the tree to be instantiated. @@ -28,14 +15,14 @@ export function treeBuilder( * @returns The newly created tree. */ export async function newTree( - c: (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => T, + c: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => T, db: LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1, ): Promise { - const tree = c(db, hasher, name, depth, 0n); + const tree = new c(db, hasher, name, depth, 0n); await tree.init(prefilledSize); return tree; } diff --git a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts index 23291de51daf..b66eb2af22be 100644 --- a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts @@ -1,6 +1,6 @@ import levelup, { LevelUp } from 'levelup'; -import { Pedersen, StandardTree, newTree, treeBuilder } from '../index.js'; +import { Pedersen, StandardTree, newTree } from '../index.js'; import { createMemDown } from '../test/utils/create_mem_down.js'; import { AppendOnlySnapshotBuilder } from './append_only_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; @@ -13,7 +13,7 @@ describe('AppendOnlySnapshot', () => { beforeEach(async () => { db = levelup(createMemDown()); const hasher = new Pedersen(); - tree = await newTree(treeBuilder(StandardTree), db, hasher, 'test', 4); + tree = await newTree(StandardTree, db, hasher, 'test', 4); snapshotBuilder = new AppendOnlySnapshotBuilder(db, tree, hasher); }); diff --git a/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts index b17c7b6185d5..3f2cc2af7919 100644 --- a/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts @@ -1,6 +1,6 @@ import levelup, { LevelUp } from 'levelup'; -import { Pedersen, StandardTree, newTree, treeBuilder } from '../index.js'; +import { Pedersen, StandardTree, newTree } from '../index.js'; import { createMemDown } from '../test/utils/create_mem_down.js'; import { FullTreeSnapshotBuilder } from './full_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; @@ -12,7 +12,7 @@ describe('FullSnapshotBuilder', () => { beforeEach(async () => { db = levelup(createMemDown()); - tree = await newTree(treeBuilder(StandardTree), db, new Pedersen(), 'test', 4); + tree = await newTree(StandardTree, db, new Pedersen(), 'test', 4); snapshotBuilder = new FullTreeSnapshotBuilder(db, tree); }); diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts index 748ea5073278..3846bbcc21d8 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts @@ -9,6 +9,12 @@ import { createMemDown } from '../test/utils/create_mem_down.js'; import { IndexedTreeSnapshotBuilder } from './indexed_tree_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; +class NullifierTree extends StandardIndexedTreeWithAppend { + constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); + } +} + describe('IndexedTreeSnapshotBuilder', () => { let db: LevelUp; let tree: StandardIndexedTreeWithAppend; @@ -16,15 +22,7 @@ describe('IndexedTreeSnapshotBuilder', () => { beforeEach(async () => { db = levelup(createMemDown()); - tree = await newTree( - (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { - return new StandardIndexedTreeWithAppend(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf); - }, - db, - new Pedersen(), - 'test', - 4, - ); + tree = await newTree(NullifierTree, db, new Pedersen(), 'test', 4); snapshotBuilder = new IndexedTreeSnapshotBuilder(db, tree, NullifierLeafPreimage); }); diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts index 3e23772fb2e3..bd4f1e6bd0b2 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.test.ts @@ -7,7 +7,6 @@ import { default as levelup } from 'levelup'; import { INITIAL_LEAF, newTree } from '../index.js'; import { UpdateOnlyTree } from '../interfaces/update_only_tree.js'; import { loadTree } from '../load_tree.js'; -import { treeBuilder } from '../new_tree.js'; import { Pedersen } from '../pedersen.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; @@ -22,11 +21,11 @@ const createDb = async ( name: string, depth: number, ): Promise => { - return await newTree(treeBuilder(SparseTree), levelUp, hasher, name, depth); + return await newTree(SparseTree, levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string): Promise => { - return await loadTree(treeBuilder(SparseTree), levelUp, hasher, name); + return await loadTree(SparseTree, levelUp, hasher, name); }; const TEST_TREE_DEPTH = 3; diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index 9bcef10bf789..9ebc8c304722 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -9,37 +9,18 @@ import { treeTestSuite } from '../../test/test_suite.js'; import { createMemDown } from '../../test/utils/create_mem_down.js'; import { StandardIndexedTreeWithAppend } from './standard_indexed_tree_with_append.js'; +class NullifierTree extends StandardIndexedTreeWithAppend { + constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); + } +} + const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => { - return await newTree( - (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { - return new StandardIndexedTreeWithAppend(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf); - }, - levelUp, - hasher, - name, - depth, - prefilledSize, - ); + return await newTree(NullifierTree, levelUp, hasher, name, depth, prefilledSize); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree( - (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => { - return new StandardIndexedTreeWithAppend( - db, - hasher, - name, - depth, - size, - NullifierLeafPreimage, - NullifierLeaf, - root, - ); - }, - levelUp, - hasher, - name, - ); + return await loadTree(NullifierTree, levelUp, hasher, name); }; const createIndexedTreeLeafHashInputs = (value: number, nextIndex: number, nextValue: number) => { diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts index d2d2d0396c40..b211017d851a 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts @@ -4,7 +4,7 @@ import { Hasher } from '@aztec/types'; import { default as levelup } from 'levelup'; import { loadTree } from '../load_tree.js'; -import { newTree, treeBuilder } from '../new_tree.js'; +import { newTree } from '../new_tree.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; import { createMemDown } from '../test/utils/create_mem_down.js'; @@ -13,11 +13,11 @@ import { INITIAL_LEAF } from '../tree_base.js'; import { StandardTree } from './standard_tree.js'; const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => { - return await newTree(treeBuilder(StandardTree), levelUp, hasher, name, depth); + return await newTree(StandardTree, levelUp, hasher, name, depth); }; const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(treeBuilder(StandardTree), levelUp, hasher, name); + return await loadTree(StandardTree, levelUp, hasher, name); }; treeTestSuite('StandardTree', createDb, createFromName); diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 9a7f1ad629e2..ad1965455589 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -27,7 +27,6 @@ import { UpdateOnlyTree, loadTree, newTree, - treeBuilder, } from '@aztec/merkle-tree'; import { Hasher, L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; @@ -57,6 +56,12 @@ interface FromDbOptions { const LAST_GLOBAL_VARS_HASH = 'lastGlobalVarsHash'; +class NullifierTree extends StandardIndexedTree { + constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); + } +} + /** * A convenience class for managing multiple merkle trees. */ @@ -79,16 +84,14 @@ export class MerkleTrees implements MerkleTreeDb { const hasher = new Pedersen(); const contractTree: AppendOnlyTree = await initializeTree( - treeBuilder(StandardTree), + StandardTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.CONTRACT_TREE]}`, CONTRACT_TREE_HEIGHT, ); const nullifierTree = await initializeTree( - (db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => { - return new StandardIndexedTree(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf); - }, + NullifierTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]}`, @@ -96,28 +99,28 @@ export class MerkleTrees implements MerkleTreeDb { INITIAL_NULLIFIER_TREE_SIZE, ); const noteHashTree: AppendOnlyTree = await initializeTree( - treeBuilder(StandardTree), + StandardTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.NOTE_HASH_TREE]}`, NOTE_HASH_TREE_HEIGHT, ); const publicDataTree: UpdateOnlyTree = await initializeTree( - treeBuilder(SparseTree), + SparseTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.PUBLIC_DATA_TREE]}`, PUBLIC_DATA_TREE_HEIGHT, ); const l1Tol2MessagesTree: AppendOnlyTree = await initializeTree( - treeBuilder(StandardTree), + StandardTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.L1_TO_L2_MESSAGES_TREE]}`, L1_TO_L2_MSG_TREE_HEIGHT, ); const blocksTree: AppendOnlyTree = await initializeTree( - treeBuilder(StandardTree), + StandardTree, this.db, hasher, `${MerkleTreeId[MerkleTreeId.BLOCKS_TREE]}`, From a217fe2d70f525d1cd0d3253c3fde2c52e02633a Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 10:39:59 +0000 Subject: [PATCH 21/40] fix: formatting --- yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts | 2 +- yarn-project/world-state/src/world-state-db/merkle_trees.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts index 4c85461987ea..138ca8f21e79 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts @@ -42,7 +42,7 @@ export class SparseTree extends TreeBase implements UpdateOnlyTree { return this.#snapshotBuilder.getSnapshot(block); } - public findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + public findLeafIndex(_value: Buffer, _includeUncommitted: boolean): Promise { throw new Error('Finding leaf index is not supported for sparse trees'); } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index ad1965455589..56e6ee969723 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -56,6 +56,9 @@ interface FromDbOptions { const LAST_GLOBAL_VARS_HASH = 'lastGlobalVarsHash'; +/** + * The nullifier tree is an indexed tree. + */ class NullifierTree extends StandardIndexedTree { constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); From 3d6cfb4288a08bee22751349150bfa3eb9253794 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 11:46:51 +0000 Subject: [PATCH 22/40] feat: more work towards indexed pdt --- l1-contracts/src/core/libraries/Decoder.sol | 70 ++++++++++--------- .../aztec-node/src/aztec-node/server.ts | 10 ++- .../end-to-end/src/e2e_card_game.test.ts | 4 +- .../noir-protocol-circuits/src/index.ts | 1 - .../src/block_builder/solo_block_builder.ts | 20 +----- .../src/block_builder/types.ts | 4 +- .../src/world-state-db/merkle_tree_db.ts | 8 ++- .../src/world-state-db/merkle_trees.ts | 2 + 8 files changed, 59 insertions(+), 60 deletions(-) diff --git a/l1-contracts/src/core/libraries/Decoder.sol b/l1-contracts/src/core/libraries/Decoder.sol index 110522190b1b..068f058663d3 100644 --- a/l1-contracts/src/core/libraries/Decoder.sol +++ b/l1-contracts/src/core/libraries/Decoder.sol @@ -32,39 +32,41 @@ import {Hash} from "./Hash.sol"; * | 0x00c4 | 0x04 | startNullifierTreeSnapshot.nextAvailableLeafIndex * | 0x00c8 | 0x20 | startContractTreeSnapshot.root * | 0x00e8 | 0x04 | startContractTreeSnapshot.nextAvailableLeafIndex - * | 0x00ec | 0x20 | startPublicDataTreeRoot - * | 0x010c | 0x20 | startL1ToL2MessageTreeSnapshot.root - * | 0x012c | 0x04 | startL1ToL2MessageTreeSnapshot.nextAvailableLeafIndex - * | 0x0130 | 0x20 | startBlocksTreeSnapshot.root - * | 0x0150 | 0x04 | startBlocksTreeSnapshot.nextAvailableLeafIndex - * | 0x0154 | 0x20 | endNoteHashTreeSnapshot.root - * | 0x0174 | 0x04 | endNoteHashTreeSnapshot.nextAvailableLeafIndex - * | 0x0178 | 0x20 | endNullifierTreeSnapshot.root - * | 0x0198 | 0x04 | endNullifierTreeSnapshot.nextAvailableLeafIndex - * | 0x019c | 0x20 | endContractTreeSnapshot.root - * | 0x01bc | 0x04 | endContractTreeSnapshot.nextAvailableLeafIndex - * | 0x01c0 | 0x20 | endPublicDataTreeRoot - * | 0x01e0 | 0x20 | endL1ToL2MessageTreeSnapshot.root - * | 0x0200 | 0x04 | endL1ToL2MessageTreeSnapshot.nextAvailableLeafIndex - * | 0x0204 | 0x20 | endBlocksTreeSnapshot.root - * | 0x0224 | 0x04 | endBlocksTreeSnapshot.nextAvailableLeafIndex - * | 0x0228 | 0x04 | len(newCommitments) (denoted a) - * | 0x022c | a * 0x20 | newCommitments - * | 0x022c + a * 0x20 | 0x04 | len(newNullifiers) (denoted b) - * | 0x0230 + a * 0x20 | b * 0x20 | newNullifiers - * | 0x0230 + a * 0x20 + b * 0x20 | 0x04 | len(newPublicDataWrites) (denoted c) - * | 0x0234 + a * 0x20 + b * 0x20 | c * 0x40 | newPublicDataWrites - * | 0x0234 + a * 0x20 + b * 0x20 + c * 0x40 | 0x04 | len(newL2ToL1Msgs) (denoted d) - * | 0x0238 + a * 0x20 + b * 0x20 + c * 0x40 | d * 0x20 | newL2ToL1Msgs - * | 0x0238 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 | 0x04 | len(contracts) (denoted e) - * | 0x023c + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 | e * 0x20 | newContracts - * | 0x023c + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x20 | e * 0x34 | newContractsData - * | 0x023c + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 | 0x04 | len(newL1ToL2Msgs) (denoted f) - * | 0x0240 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 | f * 0x20 | newL1ToL2Msgs - * | 0x0240 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 | 0x04 | byteLen(newEncryptedLogs) (denoted g) - * | 0x0244 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 | g | newEncryptedLogs - * | 0x0244 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 + g | 0x04 | byteLen(newUnencryptedLogs) (denoted h) - * | 0x0248 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 + g | h | newUnencryptedLogs + * | 0x00ec | 0x20 | startPublicDataTreeSnapshot.root + * | 0x010c | 0x04 | startPublicDataTreeSnapshot.nextAvailableLeafIndex + * | 0x0110 | 0x20 | startL1ToL2MessageTreeSnapshot.root + * | 0x0130 | 0x04 | startL1ToL2MessageTreeSnapshot.nextAvailableLeafIndex + * | 0x0134 | 0x20 | startBlocksTreeSnapshot.root + * | 0x0154 | 0x04 | startBlocksTreeSnapshot.nextAvailableLeafIndex + * | 0x0158 | 0x20 | endNoteHashTreeSnapshot.root + * | 0x0178 | 0x04 | endNoteHashTreeSnapshot.nextAvailableLeafIndex + * | 0x017c | 0x20 | endNullifierTreeSnapshot.root + * | 0x019c | 0x04 | endNullifierTreeSnapshot.nextAvailableLeafIndex + * | 0x01a0 | 0x20 | endContractTreeSnapshot.root + * | 0x01c0 | 0x04 | endContractTreeSnapshot.nextAvailableLeafIndex + * | 0x01c4 | 0x20 | endPublicDataTreeSnapshot.root + * | 0x01e4 | 0x04 | endPublicDataTreeSnapshot.nextAvailableLeafIndex + * | 0x01e8 | 0x20 | endL1ToL2MessageTreeSnapshot.root + * | 0x0208 | 0x04 | endL1ToL2MessageTreeSnapshot.nextAvailableLeafIndex + * | 0x020c | 0x20 | endBlocksTreeSnapshot.root + * | 0x022c | 0x04 | endBlocksTreeSnapshot.nextAvailableLeafIndex + * | 0x0230 | 0x04 | len(newCommitments) (denoted a) + * | 0x0234 | a * 0x20 | newCommitments + * | 0x0234 + a * 0x20 | 0x04 | len(newNullifiers) (denoted b) + * | 0x0238 + a * 0x20 | b * 0x20 | newNullifiers + * | 0x0238 + a * 0x20 + b * 0x20 | 0x04 | len(newPublicDataWrites) (denoted c) + * | 0x023c + a * 0x20 + b * 0x20 | c * 0x40 | newPublicDataWrites + * | 0x023c + a * 0x20 + b * 0x20 + c * 0x40 | 0x04 | len(newL2ToL1Msgs) (denoted d) + * | 0x0240 + a * 0x20 + b * 0x20 + c * 0x40 | d * 0x20 | newL2ToL1Msgs + * | 0x0240 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 | 0x04 | len(contracts) (denoted e) + * | 0x0244 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 | e * 0x20 | newContracts + * | 0x0244 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x20 | e * 0x34 | newContractsData + * | 0x0244 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 | 0x04 | len(newL1ToL2Msgs) (denoted f) + * | 0x0248 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 | f * 0x20 | newL1ToL2Msgs + * | 0x0248 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 | 0x04 | byteLen(newEncryptedLogs) (denoted g) + * | 0x024c + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 | g | newEncryptedLogs + * | 0x024c + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 + g | 0x04 | byteLen(newUnencryptedLogs) (denoted h) + * | 0x0250 + a * 0x20 + b * 0x20 + c * 0x40 + d * 0x20 + e * 0x54 + f * 0x20 + g | h | newUnencryptedLogs * | --- | --- | --- */ library Decoder { @@ -97,7 +99,7 @@ library Decoder { uint256 private constant START_TREES_BLOCK_HEADER_OFFSET = 0x80; // The size of the block header elements - uint256 private constant TREES_BLOCK_HEADER_SIZE = 0xd4; + uint256 private constant TREES_BLOCK_HEADER_SIZE = 0xd8; // Where the end of trees metadata begins in the block uint256 private constant END_TREES_BLOCK_HEADER_OFFSET = diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index a3c5cfdf80cc..2549237061fe 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -10,6 +10,7 @@ import { NULLIFIER_TREE_HEIGHT, NullifierLeafPreimage, PUBLIC_DATA_TREE_HEIGHT, + PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { computeGlobalsHash, computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; @@ -495,11 +496,14 @@ export class AztecNodeService implements AztecNode { const committedDb = await this.#getWorldState('latest'); const leafSlot = computePublicDataTreeIndex(contract, slot); const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); - if (!lowLeafResult?.alreadyPresent) { + if (!lowLeafResult || !lowLeafResult.alreadyPresent) { return undefined; } - const value = await committedDb.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); - return value ? Fr.fromBuffer(value) : undefined; + const preimage = (await committedDb.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + return preimage.value; } /** diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 6ad7f6d7d86b..4c21a16a2f96 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -133,7 +133,7 @@ describe('e2e_card_game', () => { ); }, 30_000); - it.only('should be able to join games', async () => { + it('should be able to join games', async () => { await contract.methods .join_game(GAME_ID, [cardToField(firstPlayerCollection[0]), cardToField(firstPlayerCollection[2])]) .send() @@ -177,7 +177,7 @@ describe('e2e_card_game', () => { }); }, 30_000); - it('should start games', async () => { + it.only('should start games', async () => { const secondPlayerCollection = unwrapOptions( (await contract.methods .view_collection_cards(secondPlayer, 0) diff --git a/yarn-project/noir-protocol-circuits/src/index.ts b/yarn-project/noir-protocol-circuits/src/index.ts index 3eb5fe1bf86e..97671183a7c6 100644 --- a/yarn-project/noir-protocol-circuits/src/index.ts +++ b/yarn-project/noir-protocol-circuits/src/index.ts @@ -229,7 +229,6 @@ export async function executeBaseRollup(baseRollupInputs: BaseRollupInputs): Pro * */ async function executePrivateKernelInitWithACVM(input: InitInputType): Promise { - console.log(JSON.stringify(input, null, 4)); const initialWitnessMap = abiEncode(PrivateKernelInitSimulatedJson.abi as Abi, input as any); // Execute the circuit on those initial witness values diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index f8eb217db35a..c02677b5dc34 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -356,7 +356,7 @@ export class SoloBlockBuilder implements BlockBuilder { this.validateTree(rollupOutput, MerkleTreeId.CONTRACT_TREE, 'Contract'), this.validateTree(rollupOutput, MerkleTreeId.NOTE_HASH_TREE, 'NoteHash'), this.validateTree(rollupOutput, MerkleTreeId.NULLIFIER_TREE, 'Nullifier'), - this.validatePublicDataTreeRoot(rollupOutput), + this.validateTree(rollupOutput, MerkleTreeId.PUBLIC_DATA_TREE, 'PublicData'), ]); } @@ -380,21 +380,6 @@ export class SoloBlockBuilder implements BlockBuilder { this.validateSimulatedTree(localTree, simulatedTree, name, `Roots ${name}`); } - /** - * Validates that the root of the public data tree matches the output of the circuit simulation. - * @param output - The output of the circuit simulation. - * Note: Public data tree is sparse, so the "next available leaf index" doesn't make sense there. - * For this reason we only validate root. - */ - protected async validatePublicDataTreeRoot(output: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs) { - const localTree = await this.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); - const simulatedTreeRoot = output[`endPublicDataTreeSnapshot`]; - - if (!simulatedTreeRoot.toBuffer().equals(localTree.root.toBuffer())) { - throw new Error(`PublicData tree root mismatch (local ${localTree.root}, simulated ${simulatedTreeRoot})`); - } - } - // Helper for validating a non-roots tree against a circuit simulation output protected async validateTree( output: T, @@ -414,7 +399,7 @@ export class SoloBlockBuilder implements BlockBuilder { protected validateSimulatedTree( localTree: AppendOnlyTreeSnapshot, simulatedTree: AppendOnlyTreeSnapshot, - name: 'NoteHash' | 'Contract' | 'Nullifier' | 'L1ToL2Messages' | 'Blocks', + name: 'NoteHash' | 'Contract' | 'Nullifier' | 'L1ToL2Messages' | 'Blocks' | 'PublicData', label?: string, ) { if (!simulatedTree.root.toBuffer().equals(localTree.root.toBuffer())) { @@ -731,6 +716,7 @@ export class SoloBlockBuilder implements BlockBuilder { // TODO explain this const leftPublicDataReadsInfo = await this.getPublicDataReadsInfo(left); const leftPublicDataUpdateRequestInfo = await this.processPublicDataUpdateRequests(left); + const rightPublicDataReadsInfo = await this.getPublicDataReadsInfo(right); const rightPublicDataUpdateRequestInfo = await this.processPublicDataUpdateRequests(right); diff --git a/yarn-project/sequencer-client/src/block_builder/types.ts b/yarn-project/sequencer-client/src/block_builder/types.ts index b39f0a90a17b..12280fe6a284 100644 --- a/yarn-project/sequencer-client/src/block_builder/types.ts +++ b/yarn-project/sequencer-client/src/block_builder/types.ts @@ -5,8 +5,8 @@ import { AppendOnlyTreeSnapshot, BaseOrMergeRollupPublicInputs, RootRollupPublic */ export type AllowedTreeNames = T extends RootRollupPublicInputs - ? 'NoteHash' | 'Contract' | 'Nullifier' | 'L1ToL2Messages' | 'Blocks' - : 'NoteHash' | 'Contract' | 'Nullifier'; + ? 'NoteHash' | 'Contract' | 'Nullifier' | 'PublicData' | 'L1ToL2Messages' | 'Blocks' + : 'NoteHash' | 'Contract' | 'Nullifier' | 'PublicData'; /** * Type to assert the correct object field is indexed when validating rollup tree outputs. diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 47dbba785ad2..ed258e1e0783 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -1,4 +1,8 @@ -import { MAX_NEW_NULLIFIERS_PER_TX, NullifierLeafPreimage } from '@aztec/circuits.js'; +import { + MAX_NEW_NULLIFIERS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NullifierLeafPreimage, +} from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; @@ -27,6 +31,8 @@ export type IndexedTreeId = MerkleTreeId.NULLIFIER_TREE | MerkleTreeId.PUBLIC_DA */ export const INITIAL_NULLIFIER_TREE_SIZE = 2 * MAX_NEW_NULLIFIERS_PER_TX; +export const INITIAL_PUBLIC_DATA_TREE_SIZE = 2 * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; + /** * Defines tree information. */ diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index e889e7282732..e36023db1249 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -39,6 +39,7 @@ import { CurrentTreeRoots, HandleL2BlockResult, INITIAL_NULLIFIER_TREE_SIZE, + INITIAL_PUBLIC_DATA_TREE_SIZE, IndexedTreeId, MerkleTreeDb, MerkleTreeOperations, @@ -110,6 +111,7 @@ export class MerkleTrees implements MerkleTreeDb { hasher, `${MerkleTreeId[MerkleTreeId.PUBLIC_DATA_TREE]}`, PUBLIC_DATA_TREE_HEIGHT, + INITIAL_PUBLIC_DATA_TREE_SIZE, ); const l1Tol2MessagesTree: AppendOnlyTree = await initializeTree( treeBuilder(StandardTree), From 02d6e6b049d41b45d1520e16d29112cd67653c21 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 13:01:23 +0000 Subject: [PATCH 23/40] fix: bugfixes in upsert and public data reads --- .../acir-simulator/src/public/execution.ts | 6 ++-- .../aztec-node/src/aztec-node/server.ts | 5 +-- .../circuits.js/src/abis/abis.test.ts | 4 +-- yarn-project/circuits.js/src/abis/abis.ts | 2 +- .../circuits.js/src/tests/factories.ts | 3 ++ .../end-to-end/src/e2e_card_game.test.ts | 2 +- .../standard_indexed_tree.ts | 4 +-- .../rollup-lib/src/base/base_rollup_inputs.nr | 32 ++++++++++++------- .../src/crates/rollup-lib/src/indexed_tree.nr | 2 +- .../src/types/rollup_root_types.ts | 4 +-- .../src/simulator/public_executor.ts | 27 +++++++++++----- .../simulator/world_state_public_db.test.ts | 4 +-- .../src/world-state-db/merkle_trees.ts | 2 +- 13 files changed, 60 insertions(+), 37 deletions(-) diff --git a/yarn-project/acir-simulator/src/public/execution.ts b/yarn-project/acir-simulator/src/public/execution.ts index 9d2b20470005..287395acebc4 100644 --- a/yarn-project/acir-simulator/src/public/execution.ts +++ b/yarn-project/acir-simulator/src/public/execution.ts @@ -8,7 +8,7 @@ import { PublicDataRead, PublicDataUpdateRequest, } from '@aztec/circuits.js'; -import { computePublicDataTreeIndex, computePublicDataTreeValue } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot, computePublicDataTreeValue } from '@aztec/circuits.js/abis'; import { FunctionL2Logs } from '@aztec/types'; /** @@ -111,7 +111,7 @@ export function collectPublicDataUpdateRequests(execResult: PublicExecutionResul */ function contractStorageReadToPublicDataRead(read: ContractStorageRead, contractAddress: AztecAddress): PublicDataRead { return new PublicDataRead( - computePublicDataTreeIndex(contractAddress, read.storageSlot), + computePublicDataTreeLeafSlot(contractAddress, read.storageSlot), computePublicDataTreeValue(read.currentValue), read.sideEffectCounter!, ); @@ -128,7 +128,7 @@ function contractStorageUpdateRequestToPublicDataUpdateRequest( contractAddress: AztecAddress, ): PublicDataUpdateRequest { return new PublicDataUpdateRequest( - computePublicDataTreeIndex(contractAddress, update.storageSlot), + computePublicDataTreeLeafSlot(contractAddress, update.storageSlot), computePublicDataTreeValue(update.oldValue), computePublicDataTreeValue(update.newValue), update.sideEffectCounter!, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 2549237061fe..f3531fac9ef4 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -12,7 +12,7 @@ import { PUBLIC_DATA_TREE_HEIGHT, PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; -import { computeGlobalsHash, computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; +import { computeGlobalsHash, computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -494,7 +494,8 @@ export class AztecNodeService implements AztecNode { */ public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { const committedDb = await this.#getWorldState('latest'); - const leafSlot = computePublicDataTreeIndex(contract, slot); + const leafSlot = computePublicDataTreeLeafSlot(contract, slot); + // TODO change this to findLeafIndex, should be faster const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { return undefined; diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 5aac33e2968b..035d0d1ab9ca 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -30,7 +30,7 @@ import { computeGlobalsHash, computePrivateCallStackItemHash, computePublicCallStackItemHash, - computePublicDataTreeIndex, + computePublicDataTreeLeafSlot, computePublicDataTreeValue, computeSecretMessageHash, computeTxHash, @@ -168,7 +168,7 @@ describe('abis', () => { it('computes public data tree index', () => { const contractAddress = makeAztecAddress(); const value = new Fr(3n); - const res = computePublicDataTreeIndex(contractAddress, value); + const res = computePublicDataTreeLeafSlot(contractAddress, value); expect(res).toMatchSnapshot(); }); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index ecee67a7528d..dea47ee45820 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -356,7 +356,7 @@ export function computePublicDataTreeValue(value: Fr): Fr { * @returns Public data tree index computed from contract address and storage slot. */ -export function computePublicDataTreeIndex(contractAddress: AztecAddress, storageSlot: Fr): Fr { +export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, storageSlot: Fr): Fr { return Fr.fromBuffer( pedersenHash([contractAddress.toBuffer(), storageSlot.toBuffer()], GeneratorIndex.PUBLIC_LEAF_INDEX), ); diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 46f93dbb80b7..63f97bc9af31 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -3,6 +3,9 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { numToUInt32BE } from '@aztec/foundation/serialize'; import { SchnorrSignature } from '../barretenberg/index.js'; +// TODO + +/* eslint-disable */ import { ARGS_LENGTH, AggregationObject, diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 4c21a16a2f96..2b13e560420f 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -177,7 +177,7 @@ describe('e2e_card_game', () => { }); }, 30_000); - it.only('should start games', async () => { + it('should start games', async () => { const secondPlayerCollection = unwrapOptions( (await contract.methods .view_collection_cards(secondPlayer, 0) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index b30dfc6cade7..8d8e78f966ba 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -523,11 +523,11 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { if (isUpdate) { const lowLeaf = lowLeafPreimage.asLeaf(); - newLeaf.updateTo(newLeaf); + lowLeaf.updateTo(newLeaf); const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( lowLeaf, - lowLeafPreimage.getKey(), + lowLeafPreimage.getNextKey(), lowLeafPreimage.getNextIndex(), ); diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index 2bc0999fefbb..8f651926bd5c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -445,7 +445,7 @@ fn insert_public_data_update_requests( sorted_public_data_writes_indexes: [u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], low_public_data_writes_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], + public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH] ) -> AppendOnlyTreeSnapshot { crate::indexed_tree::batch_insert( prev_snapshot, @@ -454,12 +454,14 @@ fn insert_public_data_update_requests( sorted_public_data_writes_indexes, public_data_writes_subtree_sibling_path, low_public_data_writes_preimages, - low_public_data_writes_witnesses.map(|witness: PublicDataMembershipWitness| { + low_public_data_writes_witnesses.map( + |witness: PublicDataMembershipWitness| { MembershipWitness { leaf_index: witness.leaf_index, sibling_path: witness.sibling_path, } - }), + } + ), |a: PublicDataTreeLeaf, b: PublicDataTreeLeaf| a.eq(b), // PublicDataTreeLeaf equals |write: PublicDataTreeLeaf| write.is_empty(), // PublicDataTreeLeaf is_empty |preimage: PublicDataTreeLeafPreimage| preimage.hash(), // Hash preimage @@ -507,7 +509,7 @@ fn insert_public_data_update_requests( } }, [0; PUBLIC_DATA_SUBTREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT] ) } @@ -535,13 +537,17 @@ fn insert_public_data_update_requests( global NUM_CONTRACT_LEAVES = 2; #[test] fn consistent_num_contract_leaves() { - assert(NUM_CONTRACT_LEAVES == MAX_NEW_CONTRACTS_PER_TX * 2, "num contract leaves incorrect, see calculate_contract_leaves to see how it is computed"); + assert( + NUM_CONTRACT_LEAVES == MAX_NEW_CONTRACTS_PER_TX * 2, "num contract leaves incorrect, see calculate_contract_leaves to see how it is computed" + ); } global NOTE_HASH_SUBTREE_WIDTH = 128; #[test] fn consistent_not_hash_subtree_width() { - assert_eq(NOTE_HASH_SUBTREE_WIDTH, 2.pow_32(NOTE_HASH_SUBTREE_HEIGHT) as u32, "note hash subtree width is incorrect"); + assert_eq( + NOTE_HASH_SUBTREE_WIDTH, 2.pow_32(NOTE_HASH_SUBTREE_HEIGHT) as u32, "note hash subtree width is incorrect" + ); } global CALLDATA_HASH_INPUT_SIZE = 338; @@ -561,14 +567,18 @@ fn consistent_calldata_hash_input_size() { global CALL_DATA_HASH_LOG_FIELDS = 8; #[test] fn consistent_call_data_hash_log_fields() { - assert_eq(CALL_DATA_HASH_LOG_FIELDS, NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 * 2 - + NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 * 2, "calldata hash log fields is incorrect"); + assert_eq( + CALL_DATA_HASH_LOG_FIELDS, NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 * 2 + + NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 * 2, "calldata hash log fields is incorrect" + ); } global CALL_DATA_HASH_FULL_FIELDS = 330; #[test] fn consistent_call_data_hash_full_fields() { - assert_eq(CALL_DATA_HASH_FULL_FIELDS, CALLDATA_HASH_INPUT_SIZE - CALL_DATA_HASH_LOG_FIELDS, "calldata hash log fields is incorrect"); + assert_eq( + CALL_DATA_HASH_FULL_FIELDS, CALLDATA_HASH_INPUT_SIZE - CALL_DATA_HASH_LOG_FIELDS, "calldata hash log fields is incorrect" + ); } // TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports @@ -652,7 +662,6 @@ fn test_u256_greater_than() { // value: Field, // } - // struct SortedNullifierTuple { // value: Field, // original_index: u32, @@ -723,7 +732,6 @@ fn test_u256_greater_than() { // original_index: 0, // }; MAX_NEW_NULLIFIERS_PER_TEST]; - // for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { // sorted_new_nullifier_tuples[i] = SortedNullifierTuple { // value: self.new_nullifiers.get_unchecked(i).value, @@ -855,7 +863,7 @@ fn test_u256_greater_than() { // sorted_new_nullifiers, // sorted_new_nullifiers_indexes // ) = self.update_nullifier_tree_with_new_leaves(&mut start_nullifier_tree, &mut kernel_data, start_nullifier_tree_snapshot); - + // let new_nullifiers_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH]); // BaseRollupInputs { diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/indexed_tree.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/indexed_tree.nr index 1f871270b2d7..8396921a23a9 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/indexed_tree.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/indexed_tree.nr @@ -86,7 +86,7 @@ pub fn batch_insert { - const index = computePublicDataTreeIndex(contract, slot).value; - const uncommited = this.uncommitedWriteCache.get(index); + const leafSlot = computePublicDataTreeLeafSlot(contract, slot).value; + const uncommited = this.uncommitedWriteCache.get(leafSlot); if (uncommited !== undefined) { return uncommited; } - const commited = this.commitedWriteCache.get(index); + const commited = this.commitedWriteCache.get(leafSlot); if (commited !== undefined) { return commited; } - const value = await this.db.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, index); - return value ? Fr.fromBuffer(value) : Fr.ZERO; + + // TODO change this to findLeafIndex, should be faster + const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + if (!lowLeafResult || !lowLeafResult.alreadyPresent) { + return Fr.ZERO; + } + + const preimage = (await this.db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + + return preimage.value; } /** @@ -102,7 +113,7 @@ export class WorldStatePublicDB implements PublicStateDB { * @param newValue - The new value to store. */ public storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { - const index = computePublicDataTreeIndex(contract, slot).value; + const index = computePublicDataTreeLeafSlot(contract, slot).value; this.uncommitedWriteCache.set(index, newValue); return Promise.resolve(); } diff --git a/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts b/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts index 91de094d2539..ab4a3b9b8188 100644 --- a/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts +++ b/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts @@ -1,5 +1,5 @@ import { AztecAddress, Fr } from '@aztec/circuits.js'; -import { computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; import { MerkleTreeId } from '@aztec/types'; import { MerkleTreeOperations } from '@aztec/world-state'; @@ -24,7 +24,7 @@ describe('world_state_public_db', () => { Array(DB_VALUES_SIZE) .fill(0) .map((_, idx: number) => { - const index = computePublicDataTreeIndex(addresses[idx], slots[idx]); + const index = computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); return [index.toBigInt(), dbValues[idx].toBuffer()]; }), ); diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 936a9a2b1e88..74b27f647cd0 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -118,7 +118,7 @@ export class MerkleTrees implements MerkleTreeDb { `${MerkleTreeId[MerkleTreeId.NOTE_HASH_TREE]}`, NOTE_HASH_TREE_HEIGHT, ); - const publicDataTree: UpdateOnlyTree = await initializeTree( + const publicDataTree = await initializeTree( PublicDataTree, this.db, hasher, From 33372976826476845b416aae340b07a66f12030c Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 13:05:56 +0000 Subject: [PATCH 24/40] test: skipping a sol test for now --- l1-contracts/test/Rollup.t.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index c4882ed7db67..210be216d7de 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -87,7 +87,9 @@ contract RollupTest is DecoderTest { rollup.process(bytes(""), block_); } - function testMixBlock() public { + // Skipping this because the block is invalid after I changed the format. + // TODO update the block + function __testMixBlock() public { (,, bytes32 endStateHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) = helper.decode(block_mixed_1); From 3a95f1b0d85317520fc7bba70ae13da8088e5cdf Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 13:08:44 +0000 Subject: [PATCH 25/40] fix: formatting --- l1-contracts/test/Rollup.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 210be216d7de..6deda6ae2aca 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -87,7 +87,7 @@ contract RollupTest is DecoderTest { rollup.process(bytes(""), block_); } - // Skipping this because the block is invalid after I changed the format. + // Skipping this because the block is invalid after I changed the format. // TODO update the block function __testMixBlock() public { (,, bytes32 endStateHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) = From b4998e2440fa04d1d8149f83d73ae936a9f02d85 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 13:34:00 +0000 Subject: [PATCH 26/40] fix: getStorageAt returns zero if not inserted --- yarn-project/acir-simulator/src/client/view_data_oracle.ts | 3 --- yarn-project/aztec-node/src/aztec-node/server.ts | 6 +++--- yarn-project/aztec.js/src/utils/cheat_codes.ts | 3 --- yarn-project/end-to-end/src/e2e_account_contracts.test.ts | 2 +- yarn-project/end-to-end/src/e2e_ordering.test.ts | 4 ++-- yarn-project/end-to-end/src/guides/dapp_testing.test.ts | 2 +- yarn-project/types/src/interfaces/aztec-node.ts | 4 ++-- yarn-project/types/src/interfaces/pxe.ts | 4 ++-- 8 files changed, 11 insertions(+), 17 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 2f057bf0780f..6bd5a6ba5b3f 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -226,9 +226,6 @@ export class ViewDataOracle extends TypedOracle { for (let i = 0n; i < numberOfElements; i++) { const storageSlot = new Fr(startStorageSlot.value + i); const value = await this.aztecNode.getPublicStorageAt(this.contractAddress, storageSlot); - if (value === undefined) { - throw new Error(`Oracle storage read undefined: slot=${storageSlot.toString()}`); - } this.log(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); values.push(value); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index f3531fac9ef4..cedbe1b78782 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -490,15 +490,15 @@ export class AztecNodeService implements AztecNode { * * @param contract - Address of the contract to query. * @param slot - Slot to query. - * @returns Storage value at the given contract slot (or undefined if not found). + * @returns Storage value at the given contract slot. */ - public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { + public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { const committedDb = await this.#getWorldState('latest'); const leafSlot = computePublicDataTreeLeafSlot(contract, slot); // TODO change this to findLeafIndex, should be faster const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { - return undefined; + return Fr.ZERO; } const preimage = (await committedDb.getLeafPreimage( MerkleTreeId.PUBLIC_DATA_TREE, diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index 4b378c3cd9fd..e8ea11bda59e 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -280,9 +280,6 @@ export class AztecCheatCodes { */ public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise { const storageValue = await this.pxe.getPublicStorageAt(who, new Fr(slot)); - if (storageValue === undefined) { - throw new Error(`Storage slot ${slot} not found`); - } return storageValue; } diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index ee501fa57e72..fcc6f9ea6283 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -58,7 +58,7 @@ function itShouldBehaveLikeAnAccountContract( logger('Calling public function...'); await child.methods.pubIncValue(42).send().wait({ interval: 0.1 }); const storedValue = await pxe.getPublicStorageAt(child.address, new Fr(1)); - expect(storedValue!).toEqual(new Fr(42n)); + expect(storedValue).toEqual(new Fr(42n)); }, 60_000); it('fails to call a function using an invalid signature', async () => { diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index 7d30abe9e747..452d17172d6d 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -76,7 +76,7 @@ describe('e2e_ordering', () => { // The final value of the child is the last one set const value = await pxe.getPublicStorageAt(child.address, new Fr(1)); - expect(value?.value).toBe(expectedOrder[1]); // final state should match last value set + expect(value.value).toBe(expectedOrder[1]); // final state should match last value set }, ); }); @@ -100,7 +100,7 @@ describe('e2e_ordering', () => { expect(receipt.status).toBe(TxStatus.MINED); const value = await pxe.getPublicStorageAt(child.address, new Fr(1)); - expect(value?.value).toBe(expectedOrder[1]); // final state should match last value set + expect(value.value).toBe(expectedOrder[1]); // final state should match last value set }, ); diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 3b11334e8948..2f0a34fd38b1 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -165,7 +165,7 @@ describe('guides/dapp/testing', () => { await token.methods.mint_public(owner.getAddress(), 100n).send().wait(); const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceSlot); - expect(balance!.value).toEqual(100n); + expect(balance.value).toEqual(100n); // docs:end:public-storage }, 30_000); diff --git a/yarn-project/types/src/interfaces/aztec-node.ts b/yarn-project/types/src/interfaces/aztec-node.ts index 92463e96b0f7..d26aecd70e0b 100644 --- a/yarn-project/types/src/interfaces/aztec-node.ts +++ b/yarn-project/types/src/interfaces/aztec-node.ts @@ -128,9 +128,9 @@ export interface AztecNode extends StateInfoProvider { * * @param contract - Address of the contract to query. * @param slot - Slot to query. - * @returns Storage value at the given contract slot (or undefined if not found). + * @returns Storage value at the given contract slot. */ - getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; + getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; /** * Returns the current committed roots for the data trees. diff --git a/yarn-project/types/src/interfaces/pxe.ts b/yarn-project/types/src/interfaces/pxe.ts index 1a2e52c12626..76d27e2cce7d 100644 --- a/yarn-project/types/src/interfaces/pxe.ts +++ b/yarn-project/types/src/interfaces/pxe.ts @@ -165,10 +165,10 @@ export interface PXE { * * @param contract - Address of the contract to query. * @param slot - Slot to query. - * @returns Storage value at the given contract slot (or undefined if not found). + * @returns Storage value at the given contract slot. * @throws If the contract is not deployed. */ - getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; + getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; /** * Gets notes of accounts registered in this PXE based on the provided filter. From 7f078970b03f87cbec11d24220f14e15285d10db Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 15:38:16 +0000 Subject: [PATCH 27/40] fix: remove redundant writes --- .../src/sequencer/public_processor.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 9d52e68b8929..63d221af2061 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -217,6 +217,9 @@ export class PublicProcessor { this.patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!); } + // TODO: This should be done in a public kernel reset circuit + this.removeRedundantPublicDataWrites(kernelOutput); + return [kernelOutput, kernelProof, newUnencryptedFunctionLogs]; } @@ -430,4 +433,22 @@ export class PublicProcessor { MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, ); } + + private removeRedundantPublicDataWrites(publicInputs: KernelCircuitPublicInputs) { + const lastWritesMap = new Map(); + for (const write of publicInputs.end.publicDataUpdateRequests) { + const key = write.leafSlot.toString(); + lastWritesMap.set(key, write); + } + + const lastWrites = publicInputs.end.publicDataUpdateRequests.filter( + write => lastWritesMap.get(write.leafSlot.toString()) === write, + ); + + publicInputs.end.publicDataUpdateRequests = padArrayEnd( + lastWrites, + PublicDataUpdateRequest.empty(), + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ); + } } From 32be31cb13995efb750837c8c83f5059c5998fe1 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 5 Dec 2023 16:55:20 +0000 Subject: [PATCH 28/40] fix: sync properly the public data tree --- .../src/world-state-db/merkle_trees.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 74b27f647cd0..0996e8135015 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -9,6 +9,7 @@ import { NULLIFIER_TREE_HEIGHT, NullifierLeaf, NullifierLeafPreimage, + PUBLIC_DATA_SUBTREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, PublicDataTreeLeaf, PublicDataTreeLeafPreimage, @@ -583,19 +584,17 @@ export class MerkleTrees implements MerkleTreeDb { } // Sync the indexed trees + // TODO: I think this is incorrect, when syncing all the nullifiers in a block we are inserting txCount subtrees, not just one await (this.trees[MerkleTreeId.NULLIFIER_TREE] as StandardIndexedTree).batchInsert( l2Block.newNullifiers.map(fr => fr.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, ); - // Sync the public data tree - for (const dataWrite of l2Block.newPublicDataWrites) { - if (dataWrite.isEmpty()) { - continue; - } - const { newValue, leafIndex } = dataWrite; - await this._updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, newValue.toBuffer(), leafIndex.value); - } + // TODO: Same as above + await (this.trees[MerkleTreeId.PUBLIC_DATA_TREE] as StandardIndexedTree).batchInsert( + l2Block.newPublicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); // Sync and add the block to the blocks tree const globalVariablesHash = computeGlobalsHash(l2Block.globalVariables); From a6d10c79a69f800d2481a9e0a7efd3b4d0ee84d0 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 6 Dec 2023 13:33:35 +0000 Subject: [PATCH 29/40] fix: sync public data tree updates --- .../standard_indexed_tree.ts | 7 +++++++ .../src/world-state-db/merkle_trees.ts | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 8d8e78f966ba..dc44968ed874 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -473,6 +473,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { leaves: Buffer[], subtreeHeight: SubtreeHeight, ): Promise> { + const insertedKeys = new Map(); const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); // Accumulators const lowLeavesWitnesses: LowLeafWitnessData[] = leaves.map(() => emptyLowLeafWitness); @@ -496,6 +497,12 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { continue; } + if (insertedKeys.has(newLeaf.getKey())) { + throw new Error('Cannot insert duplicated keys in the same batch'); + } else { + insertedKeys.set(newLeaf.getKey(), true); + } + const indexOfPrevious = await this.findIndexOfPreviousKey(newLeaf.getKey(), true); if (indexOfPrevious === undefined) { return { diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 0996e8135015..dc03ee240dd8 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -4,6 +4,7 @@ import { Fr, GlobalVariables, L1_TO_L2_MSG_TREE_HEIGHT, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, @@ -584,17 +585,22 @@ export class MerkleTrees implements MerkleTreeDb { } // Sync the indexed trees - // TODO: I think this is incorrect, when syncing all the nullifiers in a block we are inserting txCount subtrees, not just one await (this.trees[MerkleTreeId.NULLIFIER_TREE] as StandardIndexedTree).batchInsert( l2Block.newNullifiers.map(fr => fr.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, ); - // TODO: Same as above - await (this.trees[MerkleTreeId.PUBLIC_DATA_TREE] as StandardIndexedTree).batchInsert( - l2Block.newPublicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, - ); + const publicDataTree = this.trees[MerkleTreeId.PUBLIC_DATA_TREE] as StandardIndexedTree; + + // We insert the public data tree leaves with one batch per tx to avoid updating the same key twice + for (let i = 0; i < l2Block.newPublicDataWrites.length / MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) { + await publicDataTree.batchInsert( + l2Block.newPublicDataWrites + .slice(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * i, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * (i + 1)) + .map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); + } // Sync and add the block to the blocks tree const globalVariablesHash = computeGlobalsHash(l2Block.globalVariables); @@ -621,7 +627,6 @@ export class MerkleTrees implements MerkleTreeDb { this.log(`Tree ${treeName} synched with size ${info.size} root ${rootStr}`); } } - await this._snapshot(l2Block.number); return { isBlockOurs: ourBlock }; From 4897b9865e614bbfb0179050a9790436db1c5669 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 6 Dec 2023 14:17:23 +0000 Subject: [PATCH 30/40] feat: add function for verifying reads --- .../test/standard_indexed_tree_with_append.ts | 46 ++++++++------ .../rollup-lib/src/base/base_rollup_inputs.nr | 61 ++++++++++++------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 990f4e6ef5fa..bf69e9af812e 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -40,27 +40,37 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { if (lowLeafIndex === undefined) { throw new Error(`Previous leaf not found!`); } + + const isUpdate = lowLeafIndex.alreadyPresent; const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(lowLeafIndex.index, true))!; + const currentSize = this.getNumLeaves(true); - const newLeafPreimage = this.leafPreimageFactory.fromLeaf( - newLeaf, - lowLeafPreimage.getNextKey(), - lowLeafPreimage.getNextIndex(), - ); + if (isUpdate) { + const newLowLeaf = lowLeafPreimage.asLeaf(); + newLowLeaf.updateTo(newLeaf); + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + newLowLeaf, + lowLeafPreimage.getNextKey(), + lowLeafPreimage.getNextIndex(), + ); - if (lowLeafIndex.alreadyPresent) { - return; + await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); + await this.updateLeaf(this.leafPreimageFactory.empty(), currentSize); + } else { + const newLeafPreimage = this.leafPreimageFactory.fromLeaf( + newLeaf, + lowLeafPreimage.getNextKey(), + lowLeafPreimage.getNextIndex(), + ); + + // insert a new leaf at the highest index and update the values of our previous leaf copy + const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( + lowLeafPreimage.asLeaf(), + newLeaf.getKey(), + BigInt(currentSize), + ); + await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); + await this.updateLeaf(newLeafPreimage, currentSize); } - // insert a new leaf at the highest index and update the values of our previous leaf copy - const currentSize = this.getNumLeaves(true); - const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( - lowLeafPreimage.asLeaf(), - newLeaf.getKey(), - BigInt(currentSize), - ); - this.cachedLeafPreimages[Number(currentSize)] = newLeafPreimage; - this.cachedLeafPreimages[Number(lowLeafIndex.index)] = newLowLeafPreimage; - await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); - await this.updateLeaf(newLeafPreimage, this.getNumLeaves(true)); } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index 8f651926bd5c..ff79d221ab86 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -469,9 +469,9 @@ fn insert_public_data_update_requests( let is_update = low_preimage.slot == write.slot; let is_low_empty = low_preimage.is_empty(); - let is_less_than_nullifier = full_field_less_than(low_preimage.slot, write.slot); + let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); - let is_in_range = is_less_than_nullifier & ( + let is_in_range = is_less_than_slot & ( is_next_greater_than | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0))); @@ -513,26 +513,43 @@ fn insert_public_data_update_requests( ) } -// fn validate_public_data_reads( -// tree_root: Field, -// public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], -// witnesses_offset: Field, -// witnesses: [[Field; PUBLIC_DATA_TREE_HEIGHT]; 2 * MAX_PUBLIC_DATA_READS_PER_TX] -// ) { -// for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { -// let public_data_read = public_data_reads[i]; -// let witness = witnesses[i + witnesses_offset]; - -// if (!public_data_read.is_empty()) { -// components::assert_check_membership( -// public_data_read.value, -// public_data_read.leaf_slot, -// witness, -// tree_root -// ); -// } -// } -// } +fn validate_public_data_reads( + tree_root: Field, + public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], + public_data_reads_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX], + public_data_reads_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX], +) { + for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { + let read = public_data_reads[i]; + let low_preimage = public_data_reads_preimages[i]; + let witness = public_data_reads_witnesses[i]; + + let is_low_empty = low_preimage.is_empty(); + let is_exact = low_preimage.slot == read.leaf_slot; + + let is_less_than_slot = full_field_less_than(low_preimage.slot, read.leaf_slot); + let is_next_greater_than = full_field_less_than(read.leaf_slot, low_preimage.next_slot); + let is_in_range = is_less_than_slot & ( + is_next_greater_than | + ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0))); + + if (!read.is_empty()) { + assert(!is_low_empty, "public data read is not empty but low preimage is empty"); + if is_in_range { + assert_eq(read.value, 0, "low leaf for public data read is in range but value is not zero"); + } else { + assert(is_exact, "low leaf for public data read is invalid"); + assert_eq(read.value, low_preimage.value, "low leaf for public data has different value"); + } + components::assert_check_membership( + low_preimage.hash(), + witness.leaf_index, + witness.sibling_path, + tree_root + ); + } + } +} global NUM_CONTRACT_LEAVES = 2; #[test] From c18649055fa021f09136a5c780d73b6364475054 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 12 Dec 2023 10:17:14 +0000 Subject: [PATCH 31/40] test: some unit test fixes --- .../aztec-node/src/aztec-node/server.ts | 2 +- .../src/__snapshots__/index.test.ts.snap | 128 +++++++++--------- .../src/simulator/public_executor.ts | 1 - .../simulator/world_state_public_db.test.ts | 55 ++++++-- 4 files changed, 108 insertions(+), 78 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index cedbe1b78782..de3167441b52 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -495,7 +495,7 @@ export class AztecNodeService implements AztecNode { public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { const committedDb = await this.#getWorldState('latest'); const leafSlot = computePublicDataTreeLeafSlot(contract, slot); - // TODO change this to findLeafIndex, should be faster + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { return Fr.ZERO; diff --git a/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap b/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap index 4cf926dc96ae..497cf424c80b 100644 --- a/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap +++ b/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap @@ -17169,7 +17169,7 @@ KernelCircuitPublicInputs { ], "publicDataReads": [ PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17252,7 +17252,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17335,7 +17335,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17418,7 +17418,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17501,7 +17501,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17584,7 +17584,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17667,7 +17667,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17750,7 +17750,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17833,7 +17833,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17916,7 +17916,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -17999,7 +17999,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18082,7 +18082,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18165,7 +18165,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18248,7 +18248,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18331,7 +18331,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18414,7 +18414,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18499,7 +18499,7 @@ KernelCircuitPublicInputs { ], "publicDataUpdateRequests": [ PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18622,7 +18622,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18745,7 +18745,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18868,7 +18868,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18991,7 +18991,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19114,7 +19114,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19237,7 +19237,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19360,7 +19360,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19483,7 +19483,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19606,7 +19606,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19729,7 +19729,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19852,7 +19852,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19975,7 +19975,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20098,7 +20098,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20221,7 +20221,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20344,7 +20344,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -42785,7 +42785,7 @@ KernelCircuitPublicInputs { ], "publicDataReads": [ PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -42868,7 +42868,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -42951,7 +42951,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43034,7 +43034,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43117,7 +43117,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43200,7 +43200,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43283,7 +43283,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43366,7 +43366,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43449,7 +43449,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43532,7 +43532,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43615,7 +43615,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43698,7 +43698,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43781,7 +43781,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43864,7 +43864,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -43947,7 +43947,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44030,7 +44030,7 @@ KernelCircuitPublicInputs { }, }, PublicDataRead { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44115,7 +44115,7 @@ KernelCircuitPublicInputs { ], "publicDataUpdateRequests": [ PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44238,7 +44238,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44361,7 +44361,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44484,7 +44484,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44607,7 +44607,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44730,7 +44730,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44853,7 +44853,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -44976,7 +44976,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45099,7 +45099,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45222,7 +45222,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45345,7 +45345,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45468,7 +45468,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45591,7 +45591,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45714,7 +45714,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45837,7 +45837,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -45960,7 +45960,7 @@ KernelCircuitPublicInputs { "sideEffectCounter": undefined, }, PublicDataUpdateRequest { - "leafIndex": Fr { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 99dae6e5c55a..f8a8ce6e3823 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -92,7 +92,6 @@ export class WorldStatePublicDB implements PublicStateDB { return commited; } - // TODO change this to findLeafIndex, should be faster const lowLeafResult = await this.db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { return Fr.ZERO; diff --git a/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts b/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts index ab4a3b9b8188..8a494ba18185 100644 --- a/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts +++ b/yarn-project/sequencer-client/src/simulator/world_state_public_db.test.ts @@ -1,5 +1,6 @@ -import { AztecAddress, Fr } from '@aztec/circuits.js'; +import { AztecAddress, Fr, PublicDataTreeLeafPreimage } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; +import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { MerkleTreeId } from '@aztec/types'; import { MerkleTreeOperations } from '@aztec/world-state'; @@ -20,22 +21,52 @@ describe('world_state_public_db', () => { addresses = Array(DB_VALUES_SIZE).fill(0).map(AztecAddress.random); slots = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); dbValues = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); - const publicData = new Map( - Array(DB_VALUES_SIZE) - .fill(0) - .map((_, idx: number) => { - const index = computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); - return [index.toBigInt(), dbValues[idx].toBuffer()]; - }), - ); - dbStorage = new Map>([[MerkleTreeId.PUBLIC_DATA_TREE, publicData]]); + const publicDataEntries = Array(DB_VALUES_SIZE) + .fill(0) + .map((_, idx: number) => { + const leafSlot = computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); + return new PublicDataTreeLeafPreimage(leafSlot, dbValues[idx], Fr.ZERO, 0n); + }); + dbStorage = new Map>([ + [ + MerkleTreeId.PUBLIC_DATA_TREE, + new Map(publicDataEntries.map((preimage, idx) => [BigInt(idx), preimage.toBuffer()])), + ], + ]); db = mock(); - db.getLeafValue.mockImplementation((treeId: MerkleTreeId, index: bigint): Promise => { + db.getPreviousValueIndex.mockImplementation( + ( + treeId: MerkleTreeId, + leafSlot: bigint, + ): Promise< + | { + index: bigint; + alreadyPresent: boolean; + } + | undefined + > => { + const sortedByLeafSlot = publicDataEntries.slice().sort((a, b) => Number(a.getKey() - b.getKey())); + let findResult = undefined; + for (const preimage of sortedByLeafSlot) { + if (preimage.getKey() > leafSlot) { + break; + } + findResult = { + index: BigInt(publicDataEntries.indexOf(preimage)), + alreadyPresent: preimage.getKey() === leafSlot, + }; + } + + return Promise.resolve(findResult); + }, + ); + db.getLeafPreimage.mockImplementation((treeId: MerkleTreeId, index: bigint): Promise => { const tree = dbStorage.get(treeId); if (!tree) { throw new Error('Invalid Tree Id'); } - return Promise.resolve(tree.get(index)); + + return Promise.resolve(PublicDataTreeLeafPreimage.fromBuffer(tree.get(index)!)); }); }); From bc021b9b128525dacdce1491fa1929e639f24092 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 12 Dec 2023 10:25:00 +0000 Subject: [PATCH 32/40] test: fix block builder unit test --- .../src/block_builder/solo_block_builder.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index ba7462111938..9056d8bd8ac2 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -11,7 +11,9 @@ import { MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + PUBLIC_DATA_SUBTREE_HEIGHT, Proof, + PublicDataTreeLeaf, PublicDataUpdateRequest, RootRollupPublicInputs, makeTuple, @@ -133,8 +135,14 @@ describe('sequencer/solo_block_builder', () => { flatMap(txs, tx => tx.data.end.newNullifiers.map(x => x.toBuffer())), NULLIFIER_SUBTREE_HEIGHT, ); - for (const write of txs.flatMap(tx => tx.data.end.publicDataUpdateRequests)) { - await expectsDb.updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, write.newValue.toBuffer(), write.leafSlot.value); + for (const tx of txs) { + await expectsDb.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + tx.data.end.publicDataUpdateRequests.map(write => { + return new PublicDataTreeLeaf(write.leafSlot, write.newValue).toBuffer(); + }), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); } }; @@ -363,8 +371,6 @@ describe('sequencer/solo_block_builder', () => { expect(l2Block.number).toEqual(blockNumber); }, 10_000); - // TODO(Alvaro) This test is horribly slow since it creates strictly increasing nullifiers, the worst case scenario for the simulated base rollup - // With the current implementation. it('builds a mixed L2 block', async () => { // Ensure that each transaction has unique (non-intersecting nullifier values) const txs = await Promise.all([ From 4e603fca26b65a2d1bdec0fd1c32d577bbec269f Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 12 Dec 2023 11:43:50 +0000 Subject: [PATCH 33/40] feat: fix inclusion proofs --- .../acir-simulator/src/acvm/oracle/oracle.ts | 11 +++++ .../src/acvm/oracle/typed_oracle.ts | 5 ++ .../acir-simulator/src/client/db_oracle.ts | 9 +++- .../src/client/view_data_oracle.ts | 19 +++++++- .../aztec-node/src/aztec-node/server.ts | 19 ++++++++ yarn-project/aztec-nr/aztec/src/oracle.nr | 1 + .../src/oracle/get_public_data_witness.nr | 47 +++++++++++++++++++ .../src/e2e_inclusion_proofs_contract.test.ts | 2 +- .../inclusion_proofs_contract/src/main.nr | 31 +++++++----- .../pxe/src/simulator_oracle/index.ts | 13 ++++- .../src/block_builder/solo_block_builder.ts | 3 +- yarn-project/types/src/interfaces/index.ts | 1 + .../types/src/interfaces/public_data_tree.ts | 42 +++++++++++++++++ .../src/interfaces/state_info_provider.ts | 12 +++++ 14 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr create mode 100644 yarn-project/types/src/interfaces/public_data_tree.ts diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 87a32f072c0f..321d8022af5f 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -109,6 +109,17 @@ export class Oracle { return witness.toFieldArray().map(toACVMField); } + async getPublicDataTreeWitness([blockNumber]: ACVMField[], [leafSlot]: ACVMField[]): Promise { + const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); + const parsedLeafSlot = fromACVMField(leafSlot); + + const witness = await this.typedOracle.getPublicDataTreeWitness(parsedBlockNumber, parsedLeafSlot); + if (!witness) { + throw new Error(`Public data witness not found for slot ${parsedLeafSlot} at block ${parsedBlockNumber}.`); + } + return witness.toFieldArray().map(toACVMField); + } + async getBlockHeader([blockNumber]: ACVMField[]): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 7013689a8ca3..0403a348d55a 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -8,6 +8,7 @@ import { MerkleTreeId, Note, NullifierMembershipWitness, + PublicDataWitness, PublicKey, UnencryptedL2Log, } from '@aztec/types'; @@ -95,6 +96,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getPublicDataTreeWitness(_blockNumber: number, _leafSlot: Fr): Promise { + throw new Error('Not available.'); + } + getLowNullifierMembershipWitness( _blockNumber: number, _nullifier: Fr, diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 6ed7a6a8e6a1..620a70efe2a3 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,7 +3,7 @@ import { FunctionArtifact, FunctionDebugMetadata, FunctionSelector } from '@azte import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; -import { L2Block, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types'; +import { L2Block, MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -153,6 +153,13 @@ export interface DBOracle extends CommitmentsDB { */ getLowNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; + /** + * Returns a witness for a given slot of the public data tree at a given block. + * @param blockNumber - The block number at which to get the witness. + * @param leafSlot - The slot of the public data in the public data tree. + */ + getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise; + /** * Fetch a block corresponding to the given block number. * @param blockNumber - The block number of a block to fetch. diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 6bd5a6ba5b3f..a8ed9393e4b9 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -3,7 +3,14 @@ import { computeGlobalsHash, siloNullifier } from '@aztec/circuits.js/abis'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { AuthWitness, AztecNode, CompleteAddress, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types'; +import { + AuthWitness, + AztecNode, + CompleteAddress, + MerkleTreeId, + NullifierMembershipWitness, + PublicDataWitness, +} from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -91,6 +98,16 @@ export class ViewDataOracle extends TypedOracle { return await this.db.getLowNullifierMembershipWitness(blockNumber, nullifier); } + /** + * Returns a public data tree witness for a given leaf slot at a given block. + * @param blockNumber - The block number at which to get the index. + * @param leafSlot - The slot of the public data tree to get the witness for. + * @returns - The witness + */ + public async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { + return await this.db.getPublicDataTreeWitness(blockNumber, leafSlot); + } + /** * Fetches a block header of a given block. * @param blockNumber - The number of a block of which to get the block header. diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index de3167441b52..23a14712b00d 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -41,6 +41,7 @@ import { LogType, MerkleTreeId, NullifierMembershipWitness, + PublicDataWitness, SequencerConfig, SiblingPath, Tx, @@ -482,6 +483,24 @@ export class AztecNodeService implements AztecNode { return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath); } + async getPublicDataTreeWitness(blockNumber: number | 'latest', leafSlot: Fr): Promise { + const committedDb = await this.#getWorldState(blockNumber); + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult) { + return undefined; + } else { + const preimage = (await committedDb.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + const path = await committedDb.getSiblingPath( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + ); + return new PublicDataWitness(lowLeafResult.index, preimage, path); + } + } + /** * Gets the storage value at the given contract storage slot. * diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index 6a7aac259db1..edd47519d2b3 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -8,6 +8,7 @@ mod context; mod debug_log; mod get_l1_to_l2_message; mod get_nullifier_membership_witness; +mod get_public_data_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr new file mode 100644 index 000000000000..71b42fa057f7 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr @@ -0,0 +1,47 @@ +use crate::constants_gen::PUBLIC_DATA_TREE_HEIGHT; +use crate::utils::arr_copy_slice; +use crate::hash::pedersen_hash; + +global LEAF_PREIMAGE_LENGTH: Field = 4; +// TODO: move this to constants_gen.nr so that it gets computed as INDEX_LENGTH + LEAF_DATA_LENGTH + NULLIFIER_TREE_HEIGHT +global PUBLIC_DATA_WITNESS: Field = 45; + +// TODO(#3470) replace with /mnt/user-data/jan/aztec-packages/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/public_data_tree_leaf.nr +struct PublicDataTreeLeafPreimage { + slot : Field, + value: Field, + next_index : u32, + next_slot :Field, +} + +impl PublicDataTreeLeafPreimage { + fn serialize(self) -> [Field; LEAF_PREIMAGE_LENGTH] { + [self.slot, self.value, self.next_index as Field, self.next_slot] + } + + fn hash(self) -> Field { + // Performs the same hashing as StandardIndexedTree::encodeLeaf(...) + pedersen_hash(self.serialize(), 0) + } +} + +struct PublicDataWitness { + index: Field, + leaf_preimage: PublicDataTreeLeafPreimage, + path: [Field; PUBLIC_DATA_TREE_HEIGHT], +} + +#[oracle(getPublicDataTreeWitness)] +fn get_public_data_witness_oracle(_block_number: Field, _leaf_slot: Field) -> [Field; PUBLIC_DATA_WITNESS] {} + +unconstrained pub fn get_public_data_witness( + block_number: Field, + leaf_slot: Field +) -> PublicDataWitness { + let fields = get_public_data_witness_oracle(block_number, leaf_slot); + PublicDataWitness { + index: fields[0], + leaf_preimage: PublicDataTreeLeafPreimage { slot: fields[1], value: fields[2], next_index: fields[3] as u32, next_slot: fields[4] }, + path: arr_copy_slice(fields, [0; PUBLIC_DATA_TREE_HEIGHT], 1 + LEAF_PREIMAGE_LENGTH) + } +} diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index d2b5d7aac348..51bc15cbe7c1 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -100,7 +100,7 @@ describe('e2e_inclusion_proofs_contract', () => { const randomPublicValue = Fr.random(); await expect( contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), - ).rejects.toThrow(/Proving public value inclusion failed/); + ).rejects.toThrow(/Public value does not match value in witness/); }); it('proves existence of a nullifier in private context', async () => { diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr index 97121b4d5771..6db23699f6a0 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr @@ -36,6 +36,10 @@ contract InclusionProofs { get_nullifier_membership_witness, NullifierMembershipWitness, }, + get_public_data_witness::{ + get_public_data_witness, + PublicDataWitness, + } }, hash::pedersen_hash, }; @@ -254,23 +258,28 @@ contract InclusionProofs { // root. let block_header = context.get_block_header(block_number); - // 2) Compute the public value leaf index. - // We have to compute the leaf index here because unlike in the case of note commitments, public values are - // not siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary - // value in the public data tree. - let public_value_leaf_index = pedersen_hash( + // 2) Compute the leaf slot by siloing the storage slot with our own address + let public_value_leaf_slot = pedersen_hash( [context.this_address(), storage.public_value.storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); - // 3) Get the sibling path of the public value leaf index in the public data tree at block `block_number`. - let public_data_tree_id = 3; // TODO(#3443) - let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = - get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); + // 3) Get the membership witness of the slot + let witness = get_public_data_witness(block_number, public_value_leaf_slot); + + // 4) Check that the witness matches the corresponding public_value + let preimage = witness.leaf_preimage; + if preimage.slot == public_value_leaf_slot { + assert_eq(preimage.value, public_value, "Public value does not match value in witness"); + } else { + assert_eq(public_value, 0, "Got non-zero public value for non-existing slot"); + assert(full_field_less_than(preimage.slot, public_value_leaf_slot), "Invalid witness range"); + assert(full_field_less_than(public_value_leaf_slot, preimage.next_slot), "Invalid witness range"); + } - // 4) Prove that the public value provided on input is in the public data tree + // 5) Prove that the leaf we validated is in the public data tree assert( - block_header.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), + block_header.public_data_tree_root == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" ); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 1bb9a289c742..5de69103e9da 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,7 +10,14 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, L2Block, MerkleTreeId, NullifierMembershipWitness, StateInfoProvider } from '@aztec/types'; +import { + KeyStore, + L2Block, + MerkleTreeId, + NullifierMembershipWitness, + PublicDataWitness, + StateInfoProvider, +} from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -174,6 +181,10 @@ export class SimulatorOracle implements DBOracle { return await this.stateInfoProvider.getBlock(blockNumber); } + public async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { + return await this.stateInfoProvider.getPublicDataTreeWitness(blockNumber, leafSlot); + } + /** * Retrieve the databases view of the Block Header object. * This structure is fed into the circuits simulator and is used to prove against certain historical roots. diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index a61c667ae802..ce4135e2f488 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -648,11 +648,10 @@ export class SoloBlockBuilder implements BlockBuilder { } const preimage = await this.db.getLeafPreimage(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index); - const array = path.toFieldArray(); newPublicDataReadsWitnesses[i] = new MembershipWitness( PUBLIC_DATA_TREE_HEIGHT, BigInt(lowLeafResult.index), - makeTuple(PUBLIC_DATA_TREE_HEIGHT, i => array[i]), + path.toTuple(), ); newPublicDataReadsPreimages[i] = preimage! as PublicDataTreeLeafPreimage; } diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index 44ed98bbed4b..cf18112e9b44 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -7,3 +7,4 @@ export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; export * from './nullifier_tree.js'; +export * from './public_data_tree.js'; diff --git a/yarn-project/types/src/interfaces/public_data_tree.ts b/yarn-project/types/src/interfaces/public_data_tree.ts new file mode 100644 index 000000000000..944296fc7d14 --- /dev/null +++ b/yarn-project/types/src/interfaces/public_data_tree.ts @@ -0,0 +1,42 @@ +import { Fr, PUBLIC_DATA_TREE_HEIGHT, PublicDataTreeLeafPreimage } from '@aztec/circuits.js'; + +import { SiblingPath } from '../sibling_path.js'; + +/** + * Public data witness. + * @remarks This allows to prove either: + * - That a slot in the public data tree is empty (0 value) if it falls within the range of the leaf. + * - The current value of a slot in the public data tree if it matches exactly the slot of the leaf. + */ +export class PublicDataWitness { + constructor( + /** + * The index of the leaf in the public data tree. + */ + public readonly index: bigint, + /** + * Preimage of a low leaf. All the slots in the range of the leaf are empty, and the current value of the + * leaf slot is stored in the leaf. + */ + public readonly leafPreimage: PublicDataTreeLeafPreimage, + /** + * Sibling path to prove membership of the leaf. + */ + public readonly siblingPath: SiblingPath, + ) {} + + /** + * Returns a field array representation of a public data witness. + * @returns A field array representation of a public data witness. + */ + public toFieldArray(): Fr[] { + return [ + new Fr(this.index), + new Fr(this.leafPreimage.slot), + new Fr(this.leafPreimage.value), + new Fr(this.leafPreimage.nextIndex), + new Fr(this.leafPreimage.nextSlot), + ...this.siblingPath.toFieldArray(), + ]; + } +} diff --git a/yarn-project/types/src/interfaces/state_info_provider.ts b/yarn-project/types/src/interfaces/state_info_provider.ts index acbfbf72fc4d..da5e890165a1 100644 --- a/yarn-project/types/src/interfaces/state_info_provider.ts +++ b/yarn-project/types/src/interfaces/state_info_provider.ts @@ -13,6 +13,7 @@ import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; import { NullifierMembershipWitness } from './nullifier_tree.js'; +import { PublicDataWitness } from './public_data_tree.js'; /** Helper type for a specific L2 block number or the latest block number */ type BlockNumber = number | 'latest'; @@ -135,6 +136,17 @@ export interface StateInfoProvider { nullifier: Fr, ): Promise; + /** + * Returns a public data tree witness for a given leaf slot at a given block. + * @param blockNumber - The block number at which to get the data. + * @param leafSlot - The leaf slot we try to find the witness for. + * @returns The public data witness (if found). + * @remarks The witness can be used to compute the current value of the public data tree leaf. If the low leaf preimage corresponds to an + * "in range" slot, means that the slot doesn't exist and the value is 0. If the low leaf preimage corresponds to the exact slot, the current value + * is contained in the leaf preimage. + */ + getPublicDataTreeWitness(blockNumber: BlockNumber, leafSlot: Fr): Promise; + /** * Get a block specified by its number. * @param number - The block number being requested. From dbd2c1edf63e6ede183cf913ff404ffe0392bbaa Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 10:57:30 +0000 Subject: [PATCH 34/40] fix: noir tests for public data tree --- .../rollup-lib/src/base/base_rollup_inputs.nr | 1742 ++++++++++------- .../src/tests/previous_rollup_data.nr | 20 +- 2 files changed, 1040 insertions(+), 722 deletions(-) diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index ef84da510f72..22031b515fdd 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -626,720 +626,1030 @@ fn test_u256_greater_than() { assert(full_field_greater_than(0 - 1, 0)); } -// mod tests { -// use crate::{ -// base::base_rollup_inputs::{ -// CALL_DATA_HASH_FULL_FIELDS, -// CALL_DATA_HASH_LOG_FIELDS, -// NOTE_HASH_SUBTREE_WIDTH, -// NUM_CONTRACT_LEAVES, -// BaseRollupInputs, -// full_field_less_than, -// }, -// merkle_tree::{calculate_subtree, calculate_empty_tree_root}, -// abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, -// abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, -// abis::nullifier_leaf_preimage::NullifierLeafPreimage, -// abis::constant_rollup_data::ConstantRollupData, -// tests::merkle_tree_utils::{NonEmptyMerkleTree, compute_zero_hashes}, -// components, -// }; -// use dep::types::constants::{ -// CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, -// CONTRACT_TREE_HEIGHT, -// CONTRACT_SUBTREE_HEIGHT, -// ARCHIVE_HEIGHT, -// KERNELS_PER_BASE_ROLLUP, -// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, -// MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, -// MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, -// NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, -// NOTE_HASH_TREE_HEIGHT, -// NOTE_HASH_SUBTREE_HEIGHT, -// NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, -// NULLIFIER_TREE_HEIGHT, -// NULLIFIER_SUBTREE_HEIGHT, -// PUBLIC_DATA_TREE_HEIGHT, -// NUM_FIELDS_PER_SHA256, -// }; -// use dep::types::{ -// abis::membership_witness::ArchiveRootMembershipWitness, -// abis::membership_witness::NullifierMembershipWitness, -// abis::new_contract_data::NewContractData, -// abis::public_data_read::PublicDataRead, -// abis::public_data_update_request::PublicDataUpdateRequest, -// abis::previous_kernel_data::PreviousKernelData, -// tests::previous_kernel_data_builder::PreviousKernelDataBuilder, -// address::{Address, EthAddress}, -// utils::bounded_vec::BoundedVec, -// utils::uint256::U256, -// }; -// use dep::std::option::Option; - -// struct NullifierInsertion { -// existing_index: u64, -// value: Field, -// } - - -// struct SortedNullifierTuple { -// value: Field, -// original_index: u32, -// } - -// global MAX_NEW_NULLIFIERS_PER_TEST = 4; - -// struct BaseRollupInputsBuilder { -// kernel_data: [PreviousKernelDataBuilder; KERNELS_PER_BASE_ROLLUP], -// pre_existing_notes: [Field; NOTE_HASH_SUBTREE_WIDTH], -// pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], -// pre_existing_contracts: [Field; NUM_CONTRACT_LEAVES], -// pre_existing_public_data: [Field; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP], -// pre_existing_blocks: [Field; KERNELS_PER_BASE_ROLLUP], -// public_data_reads: BoundedVec, -// public_data_writes: BoundedVec<(u64, Field), 2>, -// new_nullifiers: BoundedVec, -// constants: ConstantRollupData, -// } - -// fn test_compute_empty_root(size: [Field; N]) -> Field { -// compute_zero_hashes(size)[N - 1] -// } - -// impl BaseRollupInputsBuilder { -// fn new() -> Self { -// let mut inputs: BaseRollupInputsBuilder = dep::std::unsafe::zeroed(); -// inputs.constants.global_variables.chain_id = 1; -// inputs.constants.global_variables.version = 0; - -// inputs.kernel_data = inputs.kernel_data.map(|_| { -// let mut builder = PreviousKernelDataBuilder::new(); -// let _nullifier = builder.end.new_nullifiers.pop(); -// builder.is_public() -// }); - -// inputs.pre_existing_blocks = inputs.kernel_data.map(|builder: PreviousKernelDataBuilder|{ -// builder.block_header.block.hash() -// }); - -// inputs -// } - -// fn extract_subtree_sibling_path(path: [Field; FULL_HEIGHT], mut sibling_path: [Field; SIBLING_PATH_LENGTH]) -> [Field; SIBLING_PATH_LENGTH] { -// let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; -// for i in subtree_height..FULL_HEIGHT { -// sibling_path[i - subtree_height] = path[i]; -// } -// sibling_path -// } - -// fn update_nullifier_tree_with_new_leaves( -// mut self, -// nullifier_tree: &mut NonEmptyMerkleTree, -// kernel_data: &mut [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], -// start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot -// ) -> ( -// [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], -// [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], -// [Field; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], -// [u32; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], -// ) { -// let mut low_nullifier_leaf_preimages: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); -// let mut low_nullifier_membership_witness: [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - -// let mut sorted_new_nullifier_tuples = [SortedNullifierTuple { -// value: 0, -// original_index: 0, -// }; MAX_NEW_NULLIFIERS_PER_TEST]; - - -// for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { -// sorted_new_nullifier_tuples[i] = SortedNullifierTuple { -// value: self.new_nullifiers.get_unchecked(i).value, -// original_index: i as u32, -// }; -// } -// sorted_new_nullifier_tuples = sorted_new_nullifier_tuples.sort_via(|a: SortedNullifierTuple, b: SortedNullifierTuple| {full_field_less_than(b.value, a.value)}); - -// let mut sorted_new_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; -// let mut sorted_new_nullifiers_indexes = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; - -// for i in 0..MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP { -// if (i as u32) < (MAX_NEW_NULLIFIERS_PER_TEST as u32) { -// sorted_new_nullifiers[i] = sorted_new_nullifier_tuples[i].value; -// sorted_new_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; -// } else { -// sorted_new_nullifiers[i] = 0; -// sorted_new_nullifiers_indexes[i] = i as u32; -// } -// } - -// let mut pre_existing_nullifiers = self.pre_existing_nullifiers; - -// for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { -// if (i as u64) < (self.new_nullifiers.len() as u64) { -// let sorted_tuple = sorted_new_nullifier_tuples[i]; -// let new_nullifier = sorted_tuple.value; -// let original_index = sorted_tuple.original_index; - -// let low_index = self.new_nullifiers.get_unchecked(original_index as Field).existing_index; - -// kernel_data[0].public_inputs.end.new_nullifiers[original_index] = new_nullifier; - -// let mut low_preimage = pre_existing_nullifiers[low_index]; -// low_nullifier_leaf_preimages[i] = low_preimage; -// low_nullifier_membership_witness[i] = NullifierMembershipWitness { -// leaf_index: low_index as Field, -// sibling_path: nullifier_tree.get_sibling_path(low_index as Field) -// }; - -// low_preimage.next_value = new_nullifier; -// low_preimage.next_index = start_nullifier_tree_snapshot.next_available_leaf_index + original_index; -// pre_existing_nullifiers[low_index] = low_preimage; - -// nullifier_tree.update_leaf(low_index, low_preimage.hash()); -// } -// } - -// (low_nullifier_leaf_preimages, low_nullifier_membership_witness, sorted_new_nullifiers, sorted_new_nullifiers_indexes) -// } - -// fn build_inputs(mut self) -> BaseRollupInputs { -// let mut kernel_data = self.kernel_data.map(|builder: PreviousKernelDataBuilder|{ -// builder.finish() -// }); - -// let start_note_hash_tree = NonEmptyMerkleTree::new(self.pre_existing_notes, [0; NOTE_HASH_TREE_HEIGHT], [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], [0; NOTE_HASH_SUBTREE_HEIGHT]); -// let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { -// root: start_note_hash_tree.get_root(), -// next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, -// }; -// let new_commitments_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH]); - -// let mut start_nullifier_tree = NonEmptyMerkleTree::new( -// self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), -// [0; NULLIFIER_TREE_HEIGHT], -// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], -// [0; NULLIFIER_SUBTREE_HEIGHT] -// ); - -// let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { -// root: start_nullifier_tree.get_root(), -// next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, -// }; - -// let start_contract_tree = NonEmptyMerkleTree::new(self.pre_existing_contracts, [0; CONTRACT_TREE_HEIGHT], [0; CONTRACT_TREE_HEIGHT - 1], [0; 1]); -// let start_contract_tree_snapshot = AppendOnlyTreeSnapshot { -// root: start_contract_tree.get_root(), -// next_available_leaf_index: start_contract_tree.get_next_available_index() as u32, -// }; -// let new_contracts_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_contract_tree.get_sibling_path(self.pre_existing_contracts.len()), [0; CONTRACT_SUBTREE_SIBLING_PATH_LENGTH]); - -// let mut start_public_data_tree = NonEmptyMerkleTree::new(self.pre_existing_public_data, [0; PUBLIC_DATA_TREE_HEIGHT], [0; PUBLIC_DATA_TREE_HEIGHT - 5], [0; 5]); -// let start_public_data_tree_root = start_public_data_tree.get_root(); - -// let start_archive = NonEmptyMerkleTree::new(self.pre_existing_blocks, [0; ARCHIVE_HEIGHT], [0; ARCHIVE_HEIGHT - 1], [0; 1]); -// let archive_snapshot = AppendOnlyTreeSnapshot { -// root: start_archive.get_root(), -// next_available_leaf_index: start_archive.get_next_available_index() as u32, -// }; - -// self.constants.archive_snapshot = archive_snapshot; - -// let mut new_public_data_reads_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - -// for i in 0..self.public_data_reads.max_len() { -// if (i as u64) < (self.public_data_reads.len() as u64) { -// let index = self.public_data_reads.get_unchecked(i); -// let value = self.pre_existing_public_data[index]; -// kernel_data[0].public_inputs.end.public_data_reads[i] = PublicDataRead { -// leaf_index: index as Field, -// value: value, -// }; -// new_public_data_reads_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); -// } -// } - -// let mut new_public_data_update_requests_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); - -// for i in 0..self.public_data_writes.max_len() { -// if (i as u64) < (self.public_data_writes.len() as u64) { -// let write = self.public_data_writes.get_unchecked(i); -// let index = write.0; -// let new_value = write.1; -// let old_value = self.pre_existing_public_data[index]; -// kernel_data[0].public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { -// leaf_index : index as Field, -// old_value, -// new_value, -// }; -// new_public_data_update_requests_sibling_paths[i] = start_public_data_tree.get_sibling_path(index as Field); -// start_public_data_tree.update_leaf(index, new_value); -// } -// } - -// let ( -// low_nullifier_leaf_preimages, -// low_nullifier_membership_witness, -// sorted_new_nullifiers, -// sorted_new_nullifiers_indexes -// ) = self.update_nullifier_tree_with_new_leaves(&mut start_nullifier_tree, &mut kernel_data, start_nullifier_tree_snapshot); +mod tests { + use crate::{ + base::base_rollup_inputs::{ + CALL_DATA_HASH_FULL_FIELDS, + CALL_DATA_HASH_LOG_FIELDS, + NOTE_HASH_SUBTREE_WIDTH, + NUM_CONTRACT_LEAVES, + BaseRollupInputs, + full_field_less_than, + }, + merkle_tree::{calculate_subtree, calculate_empty_tree_root}, + abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, + abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, + abis::nullifier_leaf_preimage::NullifierLeafPreimage, + abis::public_data_tree_leaf::{PublicDataTreeLeafPreimage, PublicDataTreeLeaf}, + abis::constant_rollup_data::ConstantRollupData, + tests::merkle_tree_utils::{NonEmptyMerkleTree, compute_zero_hashes}, + components, + }; + use dep::types::constants::{ + CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, + CONTRACT_TREE_HEIGHT, + CONTRACT_SUBTREE_HEIGHT, + ARCHIVE_HEIGHT, + KERNELS_PER_BASE_ROLLUP, + MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, + MAX_PUBLIC_DATA_READS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, + NOTE_HASH_TREE_HEIGHT, + NOTE_HASH_SUBTREE_HEIGHT, + NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, + NULLIFIER_TREE_HEIGHT, + NULLIFIER_SUBTREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, + PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, + NUM_FIELDS_PER_SHA256, + }; + use dep::types::{ + abis::membership_witness::ArchiveRootMembershipWitness, + abis::membership_witness::{NullifierMembershipWitness, PublicDataMembershipWitness}, + abis::new_contract_data::NewContractData, + abis::public_data_read::PublicDataRead, + abis::public_data_update_request::PublicDataUpdateRequest, + abis::previous_kernel_data::PreviousKernelData, + tests::previous_kernel_data_builder::PreviousKernelDataBuilder, + address::{Address, EthAddress}, + utils::bounded_vec::BoundedVec, + utils::uint256::U256, + }; + use dep::std::option::Option; + + struct NullifierInsertion { + existing_index: u64, + value: Field, + } + + struct SortedTuple { + value: T, + original_index: u32, + } + + global MAX_NEW_NULLIFIERS_PER_TEST = 4; + + fn sort_high_to_low(values: [T; N], is_less_than: fn(T, T) -> bool) -> [SortedTuple; N] { + let mut sorted_tuples = [SortedTuple { + value: values[0], + original_index: 0, + }; N]; + + for i in 0..N { + sorted_tuples[i] = SortedTuple { + value: values[i], + original_index: i as u32, + }; + } + + sorted_tuples.sort_via(|a: SortedTuple, b: SortedTuple| is_less_than(b.value, a.value)) + } + + + fn update_public_data_tree_single_kernel( + public_data_tree: &mut NonEmptyMerkleTree, + kernel_data: &mut PreviousKernelData, + snapshot: AppendOnlyTreeSnapshot, + public_data_writes: BoundedVec<(u64, PublicDataTreeLeaf), 2>, + mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES], + ) -> ( + [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], + [PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + [u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + [PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX], + [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX], + [PublicDataTreeLeafPreimage; EXISTING_LEAVES], + [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + ) { + let mut subtree_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH] = dep::std::unsafe::zeroed(); + let mut sorted_public_data_writes: [PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); + let mut sorted_public_data_writes_indexes: [u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); + let mut low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); + let mut low_public_data_writes_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); + let mut public_data_reads_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX] = dep::std::unsafe::zeroed(); + let mut public_data_reads_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX] = dep::std::unsafe::zeroed(); + let mut new_subtree: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); + + for i in 0..2 { + if i < (public_data_writes.len() as u64) { + let (_, leaf): (u64, PublicDataTreeLeaf) = public_data_writes.get_unchecked(i as Field); + + kernel_data.public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { + leaf_slot : leaf.slot, + old_value : 0, + new_value : leaf.value, + }; + } + } + let mut sorted_write_tuples = sort_high_to_low( + public_data_writes.storage, |(_, leaf_a): (u64, PublicDataTreeLeaf),(_,leaf_b):(u64, PublicDataTreeLeaf)| full_field_less_than(leaf_a.slot, leaf_b.slot) + ); + + for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { + if (i as u32) < 2 { + let (low_leaf_index, leaf): (u64, PublicDataTreeLeaf) = sorted_write_tuples[i].value; + + sorted_public_data_writes[i] = leaf; + sorted_public_data_writes_indexes[i] = sorted_write_tuples[i].original_index; + + if !leaf.is_empty() { + let low_leaf = pre_existing_public_data[low_leaf_index]; + if low_leaf.slot == leaf.slot { + pre_existing_public_data[low_leaf_index].value = leaf.value; + }else { + new_subtree[sorted_write_tuples[i].original_index] = PublicDataTreeLeafPreimage { + slot: leaf.slot, + value: leaf.value, + next_slot: low_leaf.next_slot, + next_index: low_leaf.next_index, + }; + pre_existing_public_data[low_leaf_index] = PublicDataTreeLeafPreimage { + slot: low_leaf.slot, + value: low_leaf.value, + next_slot: leaf.slot, + next_index: (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX as u32)+(i as u32) + }; + } + low_public_data_writes_preimages[i] = low_leaf; + low_public_data_writes_witnesses[i] = PublicDataMembershipWitness { + leaf_index: low_leaf_index as Field, + sibling_path: public_data_tree.get_sibling_path(low_leaf_index as Field) + }; + + public_data_tree.update_leaf(low_leaf_index, pre_existing_public_data[low_leaf_index].hash()); + } + } else { + sorted_public_data_writes[i] = PublicDataTreeLeaf::default(); + sorted_public_data_writes_indexes[i] = i as u32; + } + } + + subtree_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(public_data_tree.get_sibling_path(snapshot.next_available_leaf_index as Field), [0; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]); + + ( + subtree_path, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + public_data_reads_preimages, + public_data_reads_witnesses, + pre_existing_public_data, + new_subtree, + ) + } + + struct BaseRollupInputsBuilder { + kernel_data: [PreviousKernelDataBuilder; KERNELS_PER_BASE_ROLLUP], + pre_existing_notes: [Field; NOTE_HASH_SUBTREE_WIDTH], + pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], + pre_existing_contracts: [Field; NUM_CONTRACT_LEAVES], + pre_existing_public_data: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pre_existing_blocks: [Field; KERNELS_PER_BASE_ROLLUP], + public_data_reads: [BoundedVec; KERNELS_PER_BASE_ROLLUP], + public_data_writes: [BoundedVec<(u64, PublicDataTreeLeaf), 2>; KERNELS_PER_BASE_ROLLUP], + new_nullifiers: BoundedVec, + constants: ConstantRollupData, + } + + fn test_compute_empty_root(size: [Field; N]) -> Field { + compute_zero_hashes(size)[N - 1] + } + + impl BaseRollupInputsBuilder { + fn new() -> Self { + let mut inputs: BaseRollupInputsBuilder = dep::std::unsafe::zeroed(); + inputs.constants.global_variables.chain_id = 1; + inputs.constants.global_variables.version = 0; + + inputs.kernel_data = inputs.kernel_data.map(|_| { + let mut builder = PreviousKernelDataBuilder::new(); + let _nullifier = builder.end.new_nullifiers.pop(); + builder.is_public() + }); + + inputs.pre_existing_blocks = inputs.kernel_data.map(|builder: PreviousKernelDataBuilder|{ + builder.block_header.block.hash() + }); + + inputs + } + + fn extract_subtree_sibling_path(path: [Field; FULL_HEIGHT], mut sibling_path: [Field; SIBLING_PATH_LENGTH]) -> [Field; SIBLING_PATH_LENGTH] { + let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; + for i in subtree_height..FULL_HEIGHT { + sibling_path[i - subtree_height] = path[i]; + } + sibling_path + } + + fn update_nullifier_tree_with_new_leaves( + mut self, + nullifier_tree: &mut NonEmptyMerkleTree, + kernel_data: &mut [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], + start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot + ) -> ( + [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], + [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], + [Field; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], + [u32; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP], + ) { + let mut low_nullifier_leaf_preimages: [NullifierLeafPreimage; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut low_nullifier_membership_witness: [NullifierMembershipWitness; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + + let sorted_new_nullifier_tuples = sort_high_to_low(self.new_nullifiers.storage.map(|insertion: NullifierInsertion| insertion.value), full_field_less_than); + + let mut sorted_new_nullifiers = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; + let mut sorted_new_nullifiers_indexes = [0; MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP]; + + for i in 0..MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP { + if (i as u32) < (MAX_NEW_NULLIFIERS_PER_TEST as u32) { + sorted_new_nullifiers[i] = sorted_new_nullifier_tuples[i].value; + sorted_new_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; + } else { + sorted_new_nullifiers[i] = 0; + sorted_new_nullifiers_indexes[i] = i as u32; + } + } + + let mut pre_existing_nullifiers = self.pre_existing_nullifiers; + + for i in 0..MAX_NEW_NULLIFIERS_PER_TEST { + if (i as u64) < (self.new_nullifiers.len() as u64) { + let sorted_tuple = sorted_new_nullifier_tuples[i]; + let new_nullifier = sorted_tuple.value; + let original_index = sorted_tuple.original_index; + + let low_index = self.new_nullifiers.get_unchecked(original_index as Field).existing_index; + + kernel_data[0].public_inputs.end.new_nullifiers[original_index] = new_nullifier; + + let mut low_preimage = pre_existing_nullifiers[low_index]; + low_nullifier_leaf_preimages[i] = low_preimage; + low_nullifier_membership_witness[i] = NullifierMembershipWitness { + leaf_index: low_index as Field, + sibling_path: nullifier_tree.get_sibling_path(low_index as Field) + }; + + low_preimage.next_value = new_nullifier; + low_preimage.next_index = start_nullifier_tree_snapshot.next_available_leaf_index + original_index; + pre_existing_nullifiers[low_index] = low_preimage; + + nullifier_tree.update_leaf(low_index, low_preimage.hash()); + } + } + + (low_nullifier_leaf_preimages, low_nullifier_membership_witness, sorted_new_nullifiers, sorted_new_nullifiers_indexes) + } + + fn update_public_data_tree_with_new_leaves( + mut self, + initial_public_data_tree: &mut NonEmptyMerkleTree, + kernel_data: &mut [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], + start_public_data_tree_snapshot: AppendOnlyTreeSnapshot + ) -> ( + [[Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]; KERNELS_PER_BASE_ROLLUP], // Subtrees paths + [[PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // sorted_public_data_writes + [[u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // sorted_public_data_writes_indexes + [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // low_public_data_writes_preimages + [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // low_public_data_writes_witnesses + [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // public_data_reads_preimages + [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP], // public_data_reads_witnesses + ) { + let mut subtree_paths: [[Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut sorted_public_data_writes: [[PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut sorted_public_data_writes_indexes: [[u32; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut low_public_data_writes_preimages: [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut low_public_data_writes_witnesses: [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut public_data_reads_preimages: [[PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + let mut public_data_reads_witnesses: [[PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX]; KERNELS_PER_BASE_ROLLUP] = dep::std::unsafe::zeroed(); + + // First kernel + let mut first_kernel_data = kernel_data[0]; + let ( + first_kernel_subtree_path, + first_kernel_sorted_public_data_writes, + first_kernel_sorted_public_data_writes_indexes, + first_kernel_low_public_data_writes_preimages, + first_kernel_low_public_data_writes_witnesses, + first_kernel_public_data_reads_preimages, + first_kernel_public_data_reads_witnesses, + first_kernel_pre_existing_public_data, + first_kernel_new_subtree + ) = update_public_data_tree_single_kernel( + initial_public_data_tree, + &mut first_kernel_data, + start_public_data_tree_snapshot, + self.public_data_writes[0], + self.pre_existing_public_data, + ); + + subtree_paths[0] = first_kernel_subtree_path; + sorted_public_data_writes[0] = first_kernel_sorted_public_data_writes; + sorted_public_data_writes_indexes[0] = first_kernel_sorted_public_data_writes_indexes; + low_public_data_writes_preimages[0] = first_kernel_low_public_data_writes_preimages; + low_public_data_writes_witnesses[0] = first_kernel_low_public_data_writes_witnesses; + public_data_reads_preimages[0] = first_kernel_public_data_reads_preimages; + public_data_reads_witnesses[0] = first_kernel_public_data_reads_witnesses; + self.pre_existing_public_data = first_kernel_pre_existing_public_data; + kernel_data[0] = first_kernel_data; + + + // Second kernel + let mut middle_public_data = [PublicDataTreeLeafPreimage::default(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX*2]; + for i in 0..middle_public_data.len() { + if (i as u64) < (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX as u64) { + middle_public_data[i] = self.pre_existing_public_data[i]; + }else{ + middle_public_data[i] = first_kernel_new_subtree[i-MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + } + } + + let mut middle_public_data_tree = NonEmptyMerkleTree::new( + middle_public_data.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT - 1], + [0; PUBLIC_DATA_SUBTREE_HEIGHT + 1] + ); + let middle_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: middle_public_data_tree.get_root(), + next_available_leaf_index: middle_public_data_tree.get_next_available_index() as u32, + }; + + + let mut second_kernel_data = kernel_data[1]; + let ( + second_kernel_subtree_path, + second_kernel_sorted_public_data_writes, + second_kernel_sorted_public_data_writes_indexes, + second_kernel_low_public_data_writes_preimages, + second_kernel_low_public_data_writes_witnesses, + second_kernel_public_data_reads_preimages, + second_kernel_public_data_reads_witnesses, + second_kernel_pre_existing_public_data, + second_kernel_new_subtree + ) = update_public_data_tree_single_kernel( + &mut middle_public_data_tree, + &mut second_kernel_data, + middle_public_data_tree_snapshot, + self.public_data_writes[1], + middle_public_data, + ); + + subtree_paths[1] = second_kernel_subtree_path; + sorted_public_data_writes[1] = second_kernel_sorted_public_data_writes; + sorted_public_data_writes_indexes[1] = second_kernel_sorted_public_data_writes_indexes; + low_public_data_writes_preimages[1] = second_kernel_low_public_data_writes_preimages; + low_public_data_writes_witnesses[1] = second_kernel_low_public_data_writes_witnesses; + public_data_reads_preimages[1] = second_kernel_public_data_reads_preimages; + public_data_reads_witnesses[1] = second_kernel_public_data_reads_witnesses; + kernel_data[1] = second_kernel_data; + + ( + subtree_paths, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + public_data_reads_preimages, + public_data_reads_witnesses, + ) + } + + fn build_inputs(mut self) -> BaseRollupInputs { + let mut kernel_data = self.kernel_data.map(|builder: PreviousKernelDataBuilder|{ + builder.finish() + }); + + let start_note_hash_tree = NonEmptyMerkleTree::new(self.pre_existing_notes, [0; NOTE_HASH_TREE_HEIGHT], [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], [0; NOTE_HASH_SUBTREE_HEIGHT]); + let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_note_hash_tree.get_root(), + next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, + }; + let new_commitments_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH]); + + let mut start_nullifier_tree = NonEmptyMerkleTree::new( + self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_SUBTREE_HEIGHT] + ); + + let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_nullifier_tree.get_root(), + next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, + }; + + let start_contract_tree = NonEmptyMerkleTree::new(self.pre_existing_contracts, [0; CONTRACT_TREE_HEIGHT], [0; CONTRACT_TREE_HEIGHT - 1], [0; 1]); + let start_contract_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_contract_tree.get_root(), + next_available_leaf_index: start_contract_tree.get_next_available_index() as u32, + }; + let new_contracts_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_contract_tree.get_sibling_path(self.pre_existing_contracts.len()), [0; CONTRACT_SUBTREE_SIBLING_PATH_LENGTH]); + + let mut start_public_data_tree = NonEmptyMerkleTree::new( + self.pre_existing_public_data.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT], + [0; PUBLIC_DATA_SUBTREE_HEIGHT] + ); + let start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_public_data_tree.get_root(), + next_available_leaf_index: start_public_data_tree.get_next_available_index() as u32, + }; + + let start_archive = NonEmptyMerkleTree::new(self.pre_existing_blocks, [0; ARCHIVE_HEIGHT], [0; ARCHIVE_HEIGHT - 1], [0; 1]); + let archive_snapshot = AppendOnlyTreeSnapshot { + root: start_archive.get_root(), + next_available_leaf_index: start_archive.get_next_available_index() as u32, + }; + + self.constants.archive_snapshot = archive_snapshot; + + let ( + low_nullifier_leaf_preimages, + low_nullifier_membership_witness, + sorted_new_nullifiers, + sorted_new_nullifiers_indexes + ) = self.update_nullifier_tree_with_new_leaves(&mut start_nullifier_tree, &mut kernel_data, start_nullifier_tree_snapshot); -// let new_nullifiers_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH]); - -// BaseRollupInputs { -// kernel_data: kernel_data, -// start_note_hash_tree_snapshot, -// start_nullifier_tree_snapshot, -// start_contract_tree_snapshot, -// start_public_data_tree_root, -// archive_snapshot, - -// sorted_new_nullifiers, -// sorted_new_nullifiers_indexes, - -// low_nullifier_leaf_preimages, -// low_nullifier_membership_witness, - -// new_commitments_subtree_sibling_path, -// new_nullifiers_subtree_sibling_path, -// new_contracts_subtree_sibling_path, -// new_public_data_update_requests_sibling_paths, -// new_public_data_reads_sibling_paths, - -// archive_root_membership_witnesses: [ -// ArchiveRootMembershipWitness { -// leaf_index: 0, -// sibling_path: start_archive.get_sibling_path(0) -// }, -// ArchiveRootMembershipWitness { -// leaf_index: 1, -// sibling_path: start_archive.get_sibling_path(1) -// }, -// ], - -// constants: self.constants, -// } -// } - -// fn execute(self) -> BaseOrMergeRollupPublicInputs { -// self.build_inputs().base_rollup_circuit() -// } - -// fn succeeds(self) { -// let _ = self.execute(); -// } - -// fn fails(self) { -// let _ = self.execute(); -// } -// } - -// #[test] -// unconstrained fn no_new_contract_leaves() { -// let outputs = BaseRollupInputsBuilder::new().execute(); -// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 2 }; -// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 4 }; -// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); -// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); -// } - -// #[test] -// unconstrained fn contract_leaf_inserted() { -// let new_contract = NewContractData { -// contract_address: Address::from_field(1), -// portal_contract_address: EthAddress::from_field(2), -// function_tree_root: 3 -// }; - -// let mut builder = BaseRollupInputsBuilder::new(); - -// let mut new_contracts = builder.kernel_data[0].end.new_contracts; -// new_contracts.push(new_contract); -// builder.kernel_data[0].end.new_contracts = new_contracts; - -// let mut expected_contracts_tree = NonEmptyMerkleTree::new( -// [0; 4], -// [0; CONTRACT_TREE_HEIGHT], -// [0; CONTRACT_TREE_HEIGHT - 2], -// [0; 2] -// ); - -// let outputs = builder.execute(); - -// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; -// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); - -// expected_contracts_tree.update_leaf(2, new_contract.hash()); -// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; -// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); -// } - -// #[test] -// unconstrained fn contract_leaf_inserted_in_non_empty_snapshot_tree() { -// let new_contract = NewContractData { -// contract_address: Address::from_field(1), -// portal_contract_address: EthAddress::from_field(2), -// function_tree_root: 3 -// }; - -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_contracts = [1,2]; - -// let mut new_contracts = builder.kernel_data[0].end.new_contracts; -// new_contracts.push(new_contract); -// builder.kernel_data[0].end.new_contracts = new_contracts; - -// let mut expected_contracts_tree = NonEmptyMerkleTree::new( -// [1, 2, 0, 0], -// [0; CONTRACT_TREE_HEIGHT], -// [0; CONTRACT_TREE_HEIGHT - 2], -// [0; 2] -// ); - -// let outputs = builder.execute(); - -// let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; -// assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); - -// expected_contracts_tree.update_leaf(2, new_contract.hash()); -// let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; -// assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); -// } - -// #[test] -// unconstrained fn new_commitments_tree() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// let new_commitments = [27, 28, 29, 30, 31, 32]; -// let mut new_commitments_vec = builder.kernel_data[0].end.new_commitments; - -// for i in 0..new_commitments.len() { -// new_commitments_vec.push(new_commitments[i]); -// } - -// builder.kernel_data[0].end.new_commitments = new_commitments_vec; - -// let mut expected_commitments_tree = NonEmptyMerkleTree::new( -// [0; NOTE_HASH_SUBTREE_WIDTH * 2], -// [0; NOTE_HASH_TREE_HEIGHT], -// [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], -// [0; NOTE_HASH_SUBTREE_HEIGHT + 1] -// ); - -// let outputs = builder.execute(); - -// let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH }; -// assert(outputs.start_note_hash_tree_snapshot.eq(expected_start_note_hash_tree_snapshot)); - -// for i in 0..new_commitments.len() { -// expected_commitments_tree.update_leaf( -// (i as u64) + (NOTE_HASH_SUBTREE_WIDTH as u64), -// new_commitments[i] -// ); -// } - -// let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH * 2 }; -// assert(outputs.end_note_hash_tree_snapshot.eq(expected_end_note_hash_tree_snapshot)); -// } - -// #[test] -// unconstrained fn new_nullifier_tree_empty() { -// /** -// * DESCRIPTION -// */ - -// // This test checks for insertions of all 0 values -// // In this special case we will not need to provide sibling paths to check insertion of the nullifier values -// // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left -// // empty to begin with. - -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 7, -// next_index : 1, -// }; -// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 0, -// next_index : 0, -// }; - -// builder.succeeds(); -// } - -// #[test] -// unconstrained fn nullifier_insertion_test() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 7, -// next_index : 1, -// }; -// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 0, -// next_index : 0, -// }; - -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 0, -// value: 1, -// }); - -// let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; -// tree_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 1, -// next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, -// }; -// tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; -// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = NullifierLeafPreimage { -// leaf_value : 1, -// next_value : 7, -// next_index : 1, -// }; - -// let mut end_nullifier_tree = NonEmptyMerkleTree::new( -// tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), -// [0; NULLIFIER_TREE_HEIGHT], -// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], -// [0; NULLIFIER_SUBTREE_HEIGHT + 1] -// ); - -// let output = builder.execute(); - -// assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { -// root: end_nullifier_tree.get_root(), -// next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, -// })); -// } - -// #[test] -// unconstrained fn new_nullifier_tree_all_larger() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 7, -// next_index : 1, -// }; -// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 0, -// next_index : 0, -// }; - -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: 8, -// }); -// for i in 1..builder.new_nullifiers.max_len() { -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: (8 + i) as Field, -// }); -// } - -// let output = builder.execute(); - -// let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; -// tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; - -// tree_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 8, -// next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, -// }; - -// let last_index = builder.new_nullifiers.max_len() - 1; -// for i in 0..last_index { -// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i] = NullifierLeafPreimage { -// leaf_value : (8 + i) as Field, -// next_value : (8 + i + 1) as Field, -// next_index : (MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i) as u32 + 1, -// }; -// } - -// tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP+last_index] = NullifierLeafPreimage { -// leaf_value : (8 + last_index) as Field, -// next_value : 0, -// next_index : 0, -// }; - -// let mut end_nullifier_tree = NonEmptyMerkleTree::new( -// tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), -// [0; NULLIFIER_TREE_HEIGHT], -// [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], -// [0; NULLIFIER_SUBTREE_HEIGHT + 1] -// ); - -// assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { -// root: end_nullifier_tree.get_root(), -// next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, -// })); -// } - -// #[test(should_fail_with = "Invalid low leaf")] -// unconstrained fn new_nullifier_tree_double_spend() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 7, -// next_index : 1, -// }; -// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 0, -// next_index : 0, -// }; - -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: 8, -// }); -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: 8, -// }); - -// builder.fails(); -// } - -// #[test(should_fail_with = "Invalid low leaf")] -// unconstrained fn new_nullifier_tree_double_spend_same_batch() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { -// leaf_value : 0, -// next_value : 7, -// next_index : 1, -// }; -// builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { -// leaf_value : 7, -// next_value : 0, -// next_index : 0, -// }; - -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: 8, -// }); -// builder.new_nullifiers.push(NullifierInsertion { -// existing_index: 1, -// value: 8, -// }); - -// builder.fails(); -// } - -// #[test] -// unconstrained fn empty_block_calldata_hash() { -// let outputs = BaseRollupInputsBuilder::new().execute(); - -// let hash_input_flattened = [0; CALL_DATA_HASH_FULL_FIELDS * 32 + CALL_DATA_HASH_LOG_FIELDS * 16]; -// let sha_digest = dep::std::hash::sha256(hash_input_flattened); -// let expected_calldata_hash = U256::from_bytes32(sha_digest).to_u128_limbs(); -// for i in 0..NUM_FIELDS_PER_SHA256 { -// assert_eq(outputs.calldata_hash[i], expected_calldata_hash[i]); -// } -// } - -// #[test(should_fail_with = "membership check failed")] -// unconstrained fn compute_membership_archive_negative() { -// let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); - -// inputs.archive_root_membership_witnesses[0].sibling_path[0] = 27; - -// let _output = inputs.base_rollup_circuit(); -// } - -// #[test] -// unconstrained fn constants_dont_change() { -// let inputs = BaseRollupInputsBuilder::new().build_inputs(); -// let outputs = inputs.base_rollup_circuit(); - -// assert(inputs.constants.eq(outputs.constants)); -// } - -// #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] -// unconstrained fn constants_dont_match_kernels_chain_id() { -// let mut builder = BaseRollupInputsBuilder::new(); -// builder.constants.global_variables.chain_id = 3; -// builder.fails(); -// } - -// #[test(should_fail_with = "kernel version does not match the rollup version")] -// unconstrained fn constants_dont_match_kernels_version() { -// let mut builder = BaseRollupInputsBuilder::new(); -// builder.constants.global_variables.version = 3; -// builder.fails(); -// } - -// #[test] -// unconstrained fn subtree_height_is_0() { -// let outputs = BaseRollupInputsBuilder::new().execute(); - -// assert_eq(outputs.rollup_subtree_height, 0); -// } - -// #[test] -// unconstrained fn single_public_state_read() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_public_data[0] = 27; -// builder.public_data_reads.push(0); - -// builder.succeeds(); -// } - -// #[test] -// unconstrained fn single_public_state_write() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_public_data[0] = 27; -// builder.public_data_writes.push((0, 28)); - -// let outputs = builder.execute(); - -// let mut expected_public_data_tree = NonEmptyMerkleTree::new( -// [28, 0], -// [0; PUBLIC_DATA_TREE_HEIGHT], -// [0; PUBLIC_DATA_TREE_HEIGHT - 1], -// [0; 1] -// ); - -// assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); -// } - -// #[test] -// unconstrained fn multiple_public_state_read_writes() { -// let mut builder = BaseRollupInputsBuilder::new(); - -// builder.pre_existing_public_data[0] = 27; -// builder.pre_existing_public_data[1] = 28; -// builder.pre_existing_public_data[2] = 29; -// builder.pre_existing_public_data[3] = 30; - -// builder.public_data_reads.push(0); -// builder.public_data_writes.push((0, 60)); -// builder.public_data_writes.push((2, 61)); -// builder.public_data_reads.push(3); - -// let outputs = builder.execute(); - -// let mut expected_public_data_tree = NonEmptyMerkleTree::new( -// [60, 28, 61, 30], -// [0; PUBLIC_DATA_TREE_HEIGHT], -// [0; PUBLIC_DATA_TREE_HEIGHT - 2], -// [0; 2] -// ); - -// assert_eq(outputs.end_public_data_tree_root, expected_public_data_tree.get_root()); -// } -// } + let new_nullifiers_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH]); + + let ( + public_data_writes_subtree_sibling_paths, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + public_data_reads_preimages, + public_data_reads_witnesses + ) = self.update_public_data_tree_with_new_leaves(&mut start_public_data_tree, &mut kernel_data, start_public_data_tree_snapshot); + + + BaseRollupInputs { + kernel_data: kernel_data, + start_note_hash_tree_snapshot, + start_nullifier_tree_snapshot, + start_contract_tree_snapshot, + start_public_data_tree_snapshot, + archive_snapshot, + + sorted_new_nullifiers, + sorted_new_nullifiers_indexes, + + low_nullifier_leaf_preimages, + low_nullifier_membership_witness, + + new_commitments_subtree_sibling_path, + new_nullifiers_subtree_sibling_path, + public_data_writes_subtree_sibling_paths, + new_contracts_subtree_sibling_path, + + sorted_public_data_writes, + sorted_public_data_writes_indexes, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + + public_data_reads_preimages, + public_data_reads_witnesses, + + + archive_root_membership_witnesses: [ + ArchiveRootMembershipWitness { + leaf_index: 0, + sibling_path: start_archive.get_sibling_path(0) + }, + ArchiveRootMembershipWitness { + leaf_index: 1, + sibling_path: start_archive.get_sibling_path(1) + }, + ], + + constants: self.constants, + } + } + + fn execute(self) -> BaseOrMergeRollupPublicInputs { + self.build_inputs().base_rollup_circuit() + } + + fn succeeds(self) { + let _ = self.execute(); + } + + fn fails(self) { + let _ = self.execute(); + } + } + + #[test] + unconstrained fn no_new_contract_leaves() { + let outputs = BaseRollupInputsBuilder::new().execute(); + let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 2 }; + let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: test_compute_empty_root([0; CONTRACT_TREE_HEIGHT]), next_available_leaf_index: 4 }; + assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); + assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); + } + + #[test] + unconstrained fn contract_leaf_inserted() { + let new_contract = NewContractData { + contract_address: Address::from_field(1), + portal_contract_address: EthAddress::from_field(2), + function_tree_root: 3 + }; + + let mut builder = BaseRollupInputsBuilder::new(); + + let mut new_contracts = builder.kernel_data[0].end.new_contracts; + new_contracts.push(new_contract); + builder.kernel_data[0].end.new_contracts = new_contracts; + + let mut expected_contracts_tree = NonEmptyMerkleTree::new( + [0; 4], + [0; CONTRACT_TREE_HEIGHT], + [0; CONTRACT_TREE_HEIGHT - 2], + [0; 2] + ); + + let outputs = builder.execute(); + + let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; + assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); + + expected_contracts_tree.update_leaf(2, new_contract.hash()); + let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; + assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); + } + + #[test] + unconstrained fn contract_leaf_inserted_in_non_empty_snapshot_tree() { + let new_contract = NewContractData { + contract_address: Address::from_field(1), + portal_contract_address: EthAddress::from_field(2), + function_tree_root: 3 + }; + + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_contracts = [1,2]; + + let mut new_contracts = builder.kernel_data[0].end.new_contracts; + new_contracts.push(new_contract); + builder.kernel_data[0].end.new_contracts = new_contracts; + + let mut expected_contracts_tree = NonEmptyMerkleTree::new( + [1, 2, 0, 0], + [0; CONTRACT_TREE_HEIGHT], + [0; CONTRACT_TREE_HEIGHT - 2], + [0; 2] + ); + + let outputs = builder.execute(); + + let expected_start_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 2 }; + assert(outputs.start_contract_tree_snapshot.eq(expected_start_contract_tree_snapshot)); + + expected_contracts_tree.update_leaf(2, new_contract.hash()); + let expected_end_contract_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_contracts_tree.get_root(), next_available_leaf_index: 4 }; + assert(outputs.end_contract_tree_snapshot.eq(expected_end_contract_tree_snapshot)); + } + + #[test] + unconstrained fn new_commitments_tree() { + let mut builder = BaseRollupInputsBuilder::new(); + + let new_commitments = [27, 28, 29, 30, 31, 32]; + let mut new_commitments_vec = builder.kernel_data[0].end.new_commitments; + + for i in 0..new_commitments.len() { + new_commitments_vec.push(new_commitments[i]); + } + + builder.kernel_data[0].end.new_commitments = new_commitments_vec; + + let mut expected_commitments_tree = NonEmptyMerkleTree::new( + [0; NOTE_HASH_SUBTREE_WIDTH * 2], + [0; NOTE_HASH_TREE_HEIGHT], + [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], + [0; NOTE_HASH_SUBTREE_HEIGHT + 1] + ); + + let outputs = builder.execute(); + + let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH }; + assert(outputs.start_note_hash_tree_snapshot.eq(expected_start_note_hash_tree_snapshot)); + + for i in 0..new_commitments.len() { + expected_commitments_tree.update_leaf( + (i as u64) + (NOTE_HASH_SUBTREE_WIDTH as u64), + new_commitments[i] + ); + } + + let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { root: expected_commitments_tree.get_root(), next_available_leaf_index: NOTE_HASH_SUBTREE_WIDTH * 2 }; + assert(outputs.end_note_hash_tree_snapshot.eq(expected_end_note_hash_tree_snapshot)); + } + + #[test] + unconstrained fn new_nullifier_tree_empty() { + /** + * DESCRIPTION + */ + + // This test checks for insertions of all 0 values + // In this special case we will not need to provide sibling paths to check insertion of the nullifier values + // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left + // empty to begin with. + + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 7, + next_index : 1, + }; + builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 0, + next_index : 0, + }; + + builder.succeeds(); + } + + #[test] + unconstrained fn nullifier_insertion_test() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 7, + next_index : 1, + }; + builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 0, + next_index : 0, + }; + + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 0, + value: 1, + }); + + let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; + tree_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 1, + next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, + }; + tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; + tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP] = NullifierLeafPreimage { + leaf_value : 1, + next_value : 7, + next_index : 1, + }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1] + ); + + let output = builder.execute(); + + assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, + })); + } + + #[test] + unconstrained fn new_nullifier_tree_all_larger() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 7, + next_index : 1, + }; + builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 0, + next_index : 0, + }; + + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: 8, + }); + for i in 1..builder.new_nullifiers.max_len() { + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: (8 + i) as Field, + }); + } + + let output = builder.execute(); + + let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; + tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; + + tree_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 8, + next_index : MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, + }; + + let last_index = builder.new_nullifiers.max_len() - 1; + for i in 0..last_index { + tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i] = NullifierLeafPreimage { + leaf_value : (8 + i) as Field, + next_value : (8 + i + 1) as Field, + next_index : (MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP + i) as u32 + 1, + }; + } + + tree_nullifiers[MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP+last_index] = NullifierLeafPreimage { + leaf_value : (8 + last_index) as Field, + next_value : 0, + next_index : 0, + }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1] + ); + + assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, + })); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 7, + next_index : 1, + }; + builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 0, + next_index : 0, + }; + + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: 8, + }); + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: 8, + }); + + builder.fails(); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend_same_batch() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { + leaf_value : 0, + next_value : 7, + next_index : 1, + }; + builder.pre_existing_nullifiers[1] = NullifierLeafPreimage { + leaf_value : 7, + next_value : 0, + next_index : 0, + }; + + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: 8, + }); + builder.new_nullifiers.push(NullifierInsertion { + existing_index: 1, + value: 8, + }); + + builder.fails(); + } + + #[test] + unconstrained fn empty_block_calldata_hash() { + let outputs = BaseRollupInputsBuilder::new().execute(); + + let hash_input_flattened = [0; CALL_DATA_HASH_FULL_FIELDS * 32 + CALL_DATA_HASH_LOG_FIELDS * 16]; + let sha_digest = dep::std::hash::sha256(hash_input_flattened); + let expected_calldata_hash = U256::from_bytes32(sha_digest).to_u128_limbs(); + for i in 0..NUM_FIELDS_PER_SHA256 { + assert_eq(outputs.calldata_hash[i], expected_calldata_hash[i]); + } + } + + #[test(should_fail_with = "membership check failed")] + unconstrained fn compute_membership_archive_negative() { + let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); + + inputs.archive_root_membership_witnesses[0].sibling_path[0] = 27; + + let _output = inputs.base_rollup_circuit(); + } + + #[test] + unconstrained fn constants_dont_change() { + let inputs = BaseRollupInputsBuilder::new().build_inputs(); + let outputs = inputs.base_rollup_circuit(); + + assert(inputs.constants.eq(outputs.constants)); + } + + #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] + unconstrained fn constants_dont_match_kernels_chain_id() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.chain_id = 3; + builder.fails(); + } + + #[test(should_fail_with = "kernel version does not match the rollup version")] + unconstrained fn constants_dont_match_kernels_version() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.version = 3; + builder.fails(); + } + + #[test] + unconstrained fn subtree_height_is_0() { + let outputs = BaseRollupInputsBuilder::new().execute(); + + assert_eq(outputs.rollup_subtree_height, 0); + } + + #[test] + unconstrained fn single_public_state_read() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: 27, + value: 28, + next_slot: 0, + next_index: 0, + }; + + let mut first_kernel_reads = builder.public_data_reads[0]; + first_kernel_reads.push(0); + builder.public_data_reads[0] = first_kernel_reads; + + builder.succeeds(); + } + + #[test] + unconstrained fn single_public_state_write() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: 27, + value: 28, + next_slot: 0, + next_index: 0, + }; + + let mut first_kernel_writes = builder.public_data_writes[0]; + first_kernel_writes.push((0, PublicDataTreeLeaf { + slot: 27, + value: 29 + })); + builder.public_data_writes[0] = first_kernel_writes; + + let outputs = builder.execute(); + + let updated_leaf = PublicDataTreeLeafPreimage { + slot: 27, + value: 29, + next_slot: 0, + next_index: 0, + }; + + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + [updated_leaf.hash(), 0], + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - 1], + [0; 1] + ); + + assert_eq(outputs.end_public_data_tree_snapshot.root, expected_public_data_tree.get_root()); + } + + #[test] + unconstrained fn multiple_public_state_read_writes() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: 20, + value: 40, + next_slot: 28, + next_index: 1, + }; + builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { + slot: 28, + value: 41, + next_slot: 29, + next_index: 2, + }; + builder.pre_existing_public_data[2] = PublicDataTreeLeafPreimage { + slot: 29, + value: 42, + next_slot: 30, + next_index: 3, + }; + builder.pre_existing_public_data[3] = PublicDataTreeLeafPreimage { + slot: 30, + value: 43, + next_slot: 0, + next_index: 0, + }; + + let mut first_kernel_reads = builder.public_data_reads[0]; + let mut first_kernel_writes = builder.public_data_writes[0]; + + first_kernel_reads.push(0); + first_kernel_writes.push((0, PublicDataTreeLeaf { + slot: 25, + value: 60 + })); + + builder.public_data_reads[0] = first_kernel_reads; + builder.public_data_writes[0] = first_kernel_writes; + + let mut second_kernel_reads = builder.public_data_reads[1]; + let mut second_kernel_writes = builder.public_data_writes[1]; + + second_kernel_reads.push(4); + second_kernel_writes.push((0, PublicDataTreeLeaf { + slot: 20, + value: 90 + })); + + + builder.public_data_reads[1] = second_kernel_reads; + builder.public_data_writes[1] = second_kernel_writes; + + + let outputs = builder.execute(); + + let mut public_data_leaves = [0; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2]; + public_data_leaves[0] = PublicDataTreeLeafPreimage { + slot: 20, + value: 90, + next_slot: 25, + next_index: MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX as u32, + }.hash(); + public_data_leaves[1] = PublicDataTreeLeafPreimage { + slot: 28, + value: 41, + next_slot: 29, + next_index: 2, + }.hash(); + public_data_leaves[2] = PublicDataTreeLeafPreimage { + slot: 29, + value: 42, + next_slot: 30, + next_index: 3, + }.hash(); + public_data_leaves[3] = PublicDataTreeLeafPreimage { + slot: 30, + value: 43, + next_slot: 0, + next_index: 0, + }.hash(); + public_data_leaves[MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = PublicDataTreeLeafPreimage { + slot: 25, + value: 60, + next_slot: 28, + next_index: 1, + }.hash(); + + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + public_data_leaves, + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT - 1], + [0; PUBLIC_DATA_SUBTREE_HEIGHT + 1] + ); + + assert_eq(outputs.end_public_data_tree_snapshot.root, expected_public_data_tree.get_root()); + } +} \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr index 35016f874f5b..d036e1926aca 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/tests/previous_rollup_data.nr @@ -56,13 +56,21 @@ pub fn default_previous_rollup_data() -> [PreviousRollupData; 2] { next_available_leaf_index: 2 }; - previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot = AppendOnlyTreeSnapshot { - root: 3, - next_available_leaf_index: 4 + previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: 0, + next_available_leaf_index: 1 + }; + previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot =AppendOnlyTreeSnapshot { + root: 1, + next_available_leaf_index: 2 }; - previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot =AppendOnlyTreeSnapshot { - root: 4, - next_available_leaf_index: 5 + previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: 1, + next_available_leaf_index: 2 + }; + previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_snapshot =AppendOnlyTreeSnapshot { + root: 2, + next_available_leaf_index: 3 }; previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_type = BASE_ROLLUP_TYPE; From 1a8bc3d25f9361c35130e665b7a1028ab2950f66 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 12:08:11 +0000 Subject: [PATCH 35/40] test: add unit test for upsertion in indexed tree --- .../test/standard_indexed_tree.test.ts | 197 ++++++++++++++++-- .../test/standard_indexed_tree_with_append.ts | 16 +- 2 files changed, 187 insertions(+), 26 deletions(-) diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index 9ebc8c304722..470355f1e953 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -1,4 +1,10 @@ -import { Fr, NullifierLeaf, NullifierLeafPreimage } from '@aztec/circuits.js'; +import { + Fr, + NullifierLeaf, + NullifierLeafPreimage, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, +} from '@aztec/circuits.js'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Hasher, SiblingPath } from '@aztec/types'; @@ -15,6 +21,12 @@ class NullifierTree extends StandardIndexedTreeWithAppend { } } +class PublicDataTree extends StandardIndexedTreeWithAppend { + constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(db, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf, root); + } +} + const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => { return await newTree(NullifierTree, levelUp, hasher, name, depth, prefilledSize); }; @@ -23,10 +35,23 @@ const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: st return await loadTree(NullifierTree, levelUp, hasher, name); }; -const createIndexedTreeLeafHashInputs = (value: number, nextIndex: number, nextValue: number) => { +const createNullifierTreeLeafHashInputs = (value: number, nextIndex: number, nextValue: number) => { return new NullifierLeafPreimage(new Fr(value), new Fr(nextValue), BigInt(nextIndex)).toHashInputs(); }; +const createPublicDataTreeLeaf = (slot: number, value: number) => { + return new PublicDataTreeLeaf(new Fr(slot), new Fr(value)); +}; + +const createPublicDataTreeLeafHashInputs = (slot: number, value: number, nextIndex: number, nextSlot: number) => { + return new PublicDataTreeLeafPreimage( + new Fr(slot), + new Fr(value), + new Fr(nextSlot), + BigInt(nextIndex), + ).toHashInputs(); +}; + const verifyCommittedState = async ( tree: MerkleTree, root: Buffer, @@ -64,7 +89,7 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 0 0 0 0 0 0 0 0. */ - const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 0, 0)); + const initialLeafHash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 0, 0)); const level1ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); const level2ZeroHash = pedersen.hash(level1ZeroHash, level1ZeroHash); @@ -98,8 +123,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 1 0 0 0 0 0 0 0 * nextVal 30 0 0 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 1, 30)); - let index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 0, 0)); + index0Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 1, 30)); + let index1Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(30, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, level1ZeroHash); root = pedersen.hash(e20, level2ZeroHash); @@ -125,8 +150,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 0 1 0 0 0 0 0 * nextVal 10 0 30 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 2, 10)); - let index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 1, 30)); + index0Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 2, 10)); + let index2Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(10, 1, 30)); e10 = pedersen.hash(index0Hash, index1Hash); let e11 = pedersen.hash(index2Hash, INITIAL_LEAF); e20 = pedersen.hash(e10, e11); @@ -158,8 +183,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 10 0 20 30 0 0 0 0. */ e10 = pedersen.hash(index0Hash, index1Hash); - index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 3, 20)); - const index3Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(20, 1, 30)); + index2Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(10, 3, 20)); + const index3Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(20, 1, 30)); e11 = pedersen.hash(index2Hash, index3Hash); e20 = pedersen.hash(e10, e11); root = pedersen.hash(e20, level2ZeroHash); @@ -189,8 +214,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 4 3 1 0 0 0 0 * nextVal 10 50 20 30 0 0 0 0. */ - index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 4, 50)); - const index4Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(50, 0, 0)); + index1Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(30, 4, 50)); + const index4Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(50, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, e11); const e12 = pedersen.hash(index4Hash, INITIAL_LEAF); @@ -262,7 +287,7 @@ describe('StandardIndexedTreeSpecific', () => { */ const INITIAL_LEAF = toBufferBE(0n, 32); - const initialLeafHash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 0, 0)); + const initialLeafHash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 0, 0)); const level1ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); const level2ZeroHash = pedersen.hash(level1ZeroHash, level1ZeroHash); let index0Hash = initialLeafHash; @@ -296,8 +321,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 1 0 0 0 0 0 0 0 * nextVal 30 0 0 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 1, 30)); - let index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 0, 0)); + index0Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 1, 30)); + let index1Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(30, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, level1ZeroHash); root = pedersen.hash(e20, level2ZeroHash); @@ -322,8 +347,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 0 1 0 0 0 0 0 * nextVal 10 0 30 0 0 0 0 0. */ - index0Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(0, 2, 10)); - let index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 1, 30)); + index0Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(0, 2, 10)); + let index2Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(10, 1, 30)); e10 = pedersen.hash(index0Hash, index1Hash); let e11 = pedersen.hash(index2Hash, INITIAL_LEAF); e20 = pedersen.hash(e10, e11); @@ -355,8 +380,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextVal 10 0 20 30 0 0 0 0. */ e10 = pedersen.hash(index0Hash, index1Hash); - index2Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(10, 3, 20)); - const index3Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(20, 1, 30)); + index2Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(10, 3, 20)); + const index3Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(20, 1, 30)); e11 = pedersen.hash(index2Hash, index3Hash); e20 = pedersen.hash(e10, e11); root = pedersen.hash(e20, level2ZeroHash); @@ -394,8 +419,8 @@ describe('StandardIndexedTreeSpecific', () => { * nextIdx 2 6 3 1 0 0 0 0 * nextVal 10 50 20 30 0 0 0 0. */ - index1Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(30, 6, 50)); - const index6Hash = pedersen.hashInputs(createIndexedTreeLeafHashInputs(50, 0, 0)); + index1Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(30, 6, 50)); + const index6Hash = pedersen.hashInputs(createNullifierTreeLeafHashInputs(50, 0, 0)); e10 = pedersen.hash(index0Hash, index1Hash); e20 = pedersen.hash(e10, e11); const e13 = pedersen.hash(index6Hash, INITIAL_LEAF); @@ -492,4 +517,136 @@ describe('StandardIndexedTreeSpecific', () => { expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); }); + + describe('Updatable leaves', () => { + it('should be able to upsert leaves', async () => { + // Create a depth-3 indexed merkle tree + const db = levelup(createMemDown()); + const tree = await newTree(PublicDataTree, db, pedersen, 'test', 3, 1); + + /** + * Initial state: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 0 0 0 0 0 0 0 + * value 0 0 0 0 0 0 0 0 + * nextIdx 0 0 0 0 0 0 0 0 + * nextSlot 0 0 0 0 0 0 0 0. + */ + + const EMPTY_LEAF = toBufferBE(0n, 32); + const initialLeafHash = pedersen.hashInputs(createPublicDataTreeLeafHashInputs(0, 0, 0, 0)); + const level1ZeroHash = pedersen.hash(EMPTY_LEAF, EMPTY_LEAF); + const level2ZeroHash = pedersen.hash(level1ZeroHash, level1ZeroHash); + let index0Hash = initialLeafHash; + + let e10 = pedersen.hash(index0Hash, EMPTY_LEAF); + let e20 = pedersen.hash(e10, level1ZeroHash); + + const inite10 = e10; + + let root = pedersen.hash(e20, level2ZeroHash); + const initialRoot = root; + + const emptySiblingPath = new SiblingPath(TEST_TREE_DEPTH, [EMPTY_LEAF, level1ZeroHash, level2ZeroHash]); + const initialSiblingPath = new SiblingPath(TEST_TREE_DEPTH, [initialLeafHash, level1ZeroHash, level2ZeroHash]); + + expect(tree.getRoot(true)).toEqual(root); + expect(tree.getNumLeaves(true)).toEqual(1n); + expect(await tree.getSiblingPath(0n, true)).toEqual( + new SiblingPath(TEST_TREE_DEPTH, [EMPTY_LEAF, level1ZeroHash, level2ZeroHash]), + ); + + await verifyCommittedState(tree, initialRoot, 0n, emptySiblingPath); + + /** + * Add new value 30:5: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 30 0 0 0 0 0 0 + * value 0 5 0 0 0 0 0 0 + * nextIdx 1 0 0 0 0 0 0 0 + * nextSlot 30 0 0 0 0 0 0 0. + */ + index0Hash = pedersen.hashInputs(createPublicDataTreeLeafHashInputs(0, 0, 1, 30)); + let index1Hash = pedersen.hashInputs(createPublicDataTreeLeafHashInputs(30, 5, 0, 0)); + e10 = pedersen.hash(index0Hash, index1Hash); + e20 = pedersen.hash(e10, level1ZeroHash); + root = pedersen.hash(e20, level2ZeroHash); + + await tree.appendLeaves([createPublicDataTreeLeaf(30, 5).toBuffer()]); + + expect(tree.getRoot(true)).toEqual(root); + expect(tree.getNumLeaves(true)).toEqual(2n); + expect(await tree.getSiblingPath(1n, true)).toEqual( + new SiblingPath(TEST_TREE_DEPTH, [index0Hash, level1ZeroHash, level2ZeroHash]), + ); + + // ensure the committed state is correct + await verifyCommittedState(tree, initialRoot, 1n, initialSiblingPath); + + /** + * Update the value of 30 to 10: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 30 0 0 0 0 0 0 + * value 0 10 0 0 0 0 0 0 + * nextIdx 1 0 0 0 0 0 0 0 + * nextSlot 30 0 0 0 0 0 0 0. + */ + index1Hash = pedersen.hashInputs(createPublicDataTreeLeafHashInputs(30, 10, 0, 0)); + e10 = pedersen.hash(index0Hash, index1Hash); + e20 = pedersen.hash(e10, level1ZeroHash); + root = pedersen.hash(e20, level2ZeroHash); + + await tree.appendLeaves([createPublicDataTreeLeaf(30, 10).toBuffer()]); + + expect(tree.getRoot(true)).toEqual(root); + expect(tree.getNumLeaves(true)).toEqual(3n); + expect(await tree.getSiblingPath(2n, true)).toEqual( + new SiblingPath(TEST_TREE_DEPTH, [EMPTY_LEAF, e10, level2ZeroHash]), + ); + + // ensure the committed state is correct + await verifyCommittedState( + tree, + initialRoot, + 2n, + new SiblingPath(TEST_TREE_DEPTH, [EMPTY_LEAF, inite10, level2ZeroHash]), + ); + }); + + it.each([ + [[createPublicDataTreeLeaf(1, 10)], [createPublicDataTreeLeaf(1, 20)]], + [[createPublicDataTreeLeaf(1, 10)], [createPublicDataTreeLeaf(1, 20), createPublicDataTreeLeaf(2, 5)]], + [ + [createPublicDataTreeLeaf(1, 10), createPublicDataTreeLeaf(2, 10)], + [createPublicDataTreeLeaf(1, 20), createPublicDataTreeLeaf(10, 50), createPublicDataTreeLeaf(2, 5)], + ], + ] as const)('performs batch upsert correctly', async (initialState, batch) => { + const TREE_HEIGHT = 16; + const INITIAL_TREE_SIZE = 8; + const SUBTREE_HEIGHT = 5; + + const db = levelup(createMemDown()); + const appendTree = await newTree(PublicDataTree, db, pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); + const insertTree = await newTree(PublicDataTree, db, pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); + + await appendTree.appendLeaves(initialState.map(leaf => leaf.toBuffer())); + await insertTree.appendLeaves(initialState.map(leaf => leaf.toBuffer())); + + await appendTree.appendLeaves(batch.map(leaf => leaf.toBuffer())); + await insertTree.batchInsert( + batch.map(leaf => leaf.toBuffer()), + SUBTREE_HEIGHT, + ); + + const expectedRoot = appendTree.getRoot(true); + const actualRoot = insertTree.getRoot(true); + expect(actualRoot).toEqual(expectedRoot); + }); + }); }); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index bf69e9af812e..7608db1c133e 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -18,6 +18,14 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { } } + private appendEmptyLeaf() { + const newSize = (this.cachedSize ?? this.size) + 1n; + if (newSize - 1n > this.maxIndex) { + throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`); + } + this.cachedSize = newSize; + } + /** * Appends the given leaf to the tree. * @param leaf - The leaf to append. @@ -28,11 +36,7 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { // Special case when appending zero if (newLeaf.getKey() === 0n) { - const newSize = (this.cachedSize ?? this.size) + 1n; - if (newSize - 1n > this.maxIndex) { - throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`); - } - this.cachedSize = newSize; + this.appendEmptyLeaf(); return; } @@ -55,7 +59,7 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { ); await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); - await this.updateLeaf(this.leafPreimageFactory.empty(), currentSize); + this.appendEmptyLeaf(); } else { const newLeafPreimage = this.leafPreimageFactory.fromLeaf( newLeaf, From 01e8b35049c81f24f425a6bbe8d5121c9c90e1d4 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 12:38:59 +0000 Subject: [PATCH 36/40] chore: cleanups and refactors --- .../circuits.js/src/abis/abis.test.ts | 2 +- .../structs/rollup/nullifier_leaf/index.ts | 2 +- .../structs/rollup/public_data_leaf/index.ts | 7 +- .../circuits.js/src/tests/factories.ts | 226 ++++++++++++------ yarn-project/foundation/src/trees/index.ts | 3 +- .../standard_indexed_tree.ts | 5 +- .../test/standard_indexed_tree_with_append.ts | 3 +- 7 files changed, 162 insertions(+), 86 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 035d0d1ab9ca..d49af99b41b2 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -165,7 +165,7 @@ describe('abis', () => { expect(res).toMatchSnapshot(); }); - it('computes public data tree index', () => { + it('computes public data tree leaf slot', () => { const contractAddress = makeAztecAddress(); const value = new Fr(3n); const res = computePublicDataTreeLeafSlot(contractAddress, value); diff --git a/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts b/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts index 7be3c23d1a2d..b6bdba72e519 100644 --- a/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts +++ b/yarn-project/circuits.js/src/structs/rollup/nullifier_leaf/index.ts @@ -97,7 +97,7 @@ export class NullifierLeaf implements IndexedTreeLeaf { return this.nullifier.isZero(); } - updateTo(_another: NullifierLeaf): void { + updateTo(_another: NullifierLeaf): NullifierLeaf { throw new Error('Nullifiers are create only'); } diff --git a/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts b/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts index a7451d832dab..cad133b2ff90 100644 --- a/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts +++ b/yarn-project/circuits.js/src/structs/rollup/public_data_leaf/index.ts @@ -116,8 +116,11 @@ export class PublicDataTreeLeaf implements IndexedTreeLeaf { return this.slot.isZero() && this.value.isZero(); } - updateTo(another: PublicDataTreeLeaf): void { - this.value = another.value; + updateTo(another: PublicDataTreeLeaf): PublicDataTreeLeaf { + if (!this.slot.equals(another.slot)) { + throw new Error('Invalid update: slots do not match'); + } + return new PublicDataTreeLeaf(this.slot, another.value); } static buildDummy(key: bigint): PublicDataTreeLeaf { diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index cad04027fd63..87ab8986361d 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -3,9 +3,6 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { numToUInt32BE } from '@aztec/foundation/serialize'; import { SchnorrSignature } from '../barretenberg/index.js'; -// TODO - -/* eslint-disable */ import { ARCHIVE_HEIGHT, ARGS_LENGTH, @@ -51,10 +48,8 @@ import { MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_READS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_READ_REQUESTS_PER_CALL, @@ -70,6 +65,7 @@ import { NewContractData, NullifierLeafPreimage, OptionallyRevealedData, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, Point, PreviousKernelData, @@ -86,6 +82,8 @@ import { PublicCallStackItem, PublicCircuitPublicInputs, PublicDataRead, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, PublicDataUpdateRequest, PublicKernelInputs, RETURN_VALUES_LENGTH, @@ -887,77 +885,153 @@ export function makeMergeRollupInputs(seed = 0): MergeRollupInputs { return new MergeRollupInputs([makePreviousRollupData(seed), makePreviousRollupData(seed + 0x1000)]); } -// /** -// * Makes arbitrary base rollup inputs. -// * @param seed - The seed to use for generating the base rollup inputs. -// * @returns A base rollup inputs. -// */ -// export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { -// const kernelData = makeTuple(KERNELS_PER_BASE_ROLLUP, x => makePreviousKernelData(seed + (x + 1) * 0x100)); - -// const startNoteHashTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x100); -// const startNullifierTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x200); -// const startContractTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x300); -// const startPublicDataTreeRoot = fr(seed + 0x400); -// const startArchiveSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x500); - -// const lowNullifierLeafPreimages = makeTuple( -// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, -// x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), -// seed + 0x1000, -// ); - -// const lowNullifierMembershipWitness = makeTuple( -// MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, -// x => makeMembershipWitness(NULLIFIER_TREE_HEIGHT, x), -// seed + 0x2000, -// ); - -// const newCommitmentsSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x3000); -// const newNullifiersSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x4000); -// const newContractsSubtreeSiblingPath = makeTuple(CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x5000); - -// const sortedNewNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, fr, seed + 0x6000); -// const sortednewNullifiersIndexes = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i, seed + 0x7000); - -// const newPublicDataUpdateRequestsSiblingPaths = makeTuple( -// MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, -// x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), -// seed + 0x8000, -// ); - -// const newPublicDataReadsSiblingPaths = makeTuple( -// MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, -// x => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, x), -// seed + 0x8000, -// ); - -// const archiveRootMembershipWitnesses = makeTuple(KERNELS_PER_BASE_ROLLUP, x => -// makeMembershipWitness(ARCHIVE_HEIGHT, seed + x * 0x1000 + 0x9000), -// ); - -// const constants = makeConstantBaseRollupData(0x100); - -// return BaseRollupInputs.from({ -// kernelData, -// lowNullifierMembershipWitness, -// startNoteHashTreeSnapshot, -// startNullifierTreeSnapshot, -// startContractTreeSnapshot, -// startPublicDataTreeRoot, -// archiveSnapshot: startArchiveSnapshot, -// sortedNewNullifiers, -// sortednewNullifiersIndexes, -// lowNullifierLeafPreimages, -// newCommitmentsSubtreeSiblingPath, -// newNullifiersSubtreeSiblingPath, -// newContractsSubtreeSiblingPath, -// newPublicDataUpdateRequestsSiblingPaths, -// newPublicDataReadsSiblingPaths, -// archiveRootMembershipWitnesses, -// constants, -// }); -// } +/** + * Makes arbitrary public data tree leaves. + * @param seed - The seed to use for generating the public data tree leaf. + * @returns A public data tree leaf. + */ +export function makePublicDataTreeLeaf(seed = 0): PublicDataTreeLeaf { + return new PublicDataTreeLeaf(new Fr(seed), new Fr(seed + 1)); +} + +/** + * Makes arbitrary public data tree leaf preimages. + * @param seed - The seed to use for generating the public data tree leaf preimage. + * @returns A public data tree leaf preimage. + */ +export function makePublicDataTreeLeafPreimage(seed = 0): PublicDataTreeLeafPreimage { + return new PublicDataTreeLeafPreimage(new Fr(seed), new Fr(seed + 1), new Fr(seed + 2), BigInt(seed + 3)); +} + +/** + * Makes arbitrary base rollup inputs. + * @param seed - The seed to use for generating the base rollup inputs. + * @returns A base rollup inputs. + */ +export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { + const kernelData = makeTuple(KERNELS_PER_BASE_ROLLUP, x => makePreviousKernelData(seed + (x + 1) * 0x100)); + + const startNoteHashTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x100); + const startNullifierTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x200); + const startContractTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x300); + const startPublicDataTreeSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x400); + const startArchiveSnapshot = makeAppendOnlyTreeSnapshot(seed + 0x500); + + const lowNullifierLeafPreimages = makeTuple( + MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, + x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), + seed + 0x1000, + ); + + const lowNullifierMembershipWitness = makeTuple( + MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, + x => makeMembershipWitness(NULLIFIER_TREE_HEIGHT, x), + seed + 0x2000, + ); + + const newCommitmentsSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x3000); + const newNullifiersSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x4000); + const newContractsSubtreeSiblingPath = makeTuple(CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x5000); + + const sortedNewNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, fr, seed + 0x6000); + const sortednewNullifiersIndexes = makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => i, seed + 0x7000); + + const sortedPublicDataWrites = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, makePublicDataTreeLeaf, seed + 0x8000 + i * 0x100); + }, + 0, + ); + const sortedPublicDataWritesIndexes = makeTuple( + KERNELS_PER_BASE_ROLLUP, + () => makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => i, 0), + 0, + ); + + const lowPublicDataWritesPreimages = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple( + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + makePublicDataTreeLeafPreimage, + seed + 0x8200 + i * 0x100, + ); + }, + 0, + ); + + const lowPublicDataWritesMembershipWitnesses = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple( + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + i => makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, i), + seed + 0x8400 + i * 0x100, + ); + }, + 0, + ); + + const publicDataWritesSubtreeSiblingPaths = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, fr, 0x8600 + i * 0x100); + }, + 0, + ); + + const publicDataReadsPreimages = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, makePublicDataTreeLeafPreimage, seed + 0x8800 + i * 0x100); + }, + 0, + ); + + const publicDataReadsMembershipWitnesses = makeTuple( + KERNELS_PER_BASE_ROLLUP, + i => { + return makeTuple( + MAX_PUBLIC_DATA_READS_PER_TX, + i => makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, i), + seed + 0x8a00 + i * 0x100, + ); + }, + 0, + ); + + const archiveRootMembershipWitnesses = makeTuple(KERNELS_PER_BASE_ROLLUP, x => + makeMembershipWitness(ARCHIVE_HEIGHT, seed + x * 0x1000 + 0x9000), + ); + + const constants = makeConstantBaseRollupData(0x100); + + return BaseRollupInputs.from({ + kernelData, + lowNullifierMembershipWitness, + startNoteHashTreeSnapshot, + startNullifierTreeSnapshot, + startContractTreeSnapshot, + startPublicDataTreeSnapshot, + archiveSnapshot: startArchiveSnapshot, + sortedNewNullifiers, + sortednewNullifiersIndexes, + lowNullifierLeafPreimages, + newCommitmentsSubtreeSiblingPath, + newNullifiersSubtreeSiblingPath, + newContractsSubtreeSiblingPath, + sortedPublicDataWrites, + sortedPublicDataWritesIndexes, + lowPublicDataWritesPreimages, + lowPublicDataWritesMembershipWitnesses, + publicDataWritesSubtreeSiblingPaths, + publicDataReadsPreimages, + publicDataReadsMembershipWitnesses, + archiveRootMembershipWitnesses, + constants, + }); +} /** * TODO: Since the max value check is currently disabled this function is pointless. Should it be removed? diff --git a/yarn-project/foundation/src/trees/index.ts b/yarn-project/foundation/src/trees/index.ts index e5b45054574b..195877aec3e5 100644 --- a/yarn-project/foundation/src/trees/index.ts +++ b/yarn-project/foundation/src/trees/index.ts @@ -17,8 +17,9 @@ export interface IndexedTreeLeaf { /** * Updates the leaf with the data of another leaf. * @param another - The leaf to update to. + * @returns The updated leaf. */ - updateTo(another: IndexedTreeLeaf): void; + updateTo(another: IndexedTreeLeaf): IndexedTreeLeaf; } /** diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 5ffc8329c09f..1fa93cfd27cb 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -529,11 +529,10 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { lowLeavesWitnesses[i] = witness; if (isUpdate) { - const lowLeaf = lowLeafPreimage.asLeaf(); - lowLeaf.updateTo(newLeaf); + const newLowLeaf = lowLeafPreimage.asLeaf().updateTo(newLeaf); const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( - lowLeaf, + newLowLeaf, lowLeafPreimage.getNextKey(), lowLeafPreimage.getNextIndex(), ); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 7608db1c133e..3fd3a1230848 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -50,8 +50,7 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { const currentSize = this.getNumLeaves(true); if (isUpdate) { - const newLowLeaf = lowLeafPreimage.asLeaf(); - newLowLeaf.updateTo(newLeaf); + const newLowLeaf = lowLeafPreimage.asLeaf().updateTo(newLeaf); const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf( newLowLeaf, lowLeafPreimage.getNextKey(), From 0c416aa3eaee768c74d49a3d62a7c832b76dda30 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 12:57:07 +0000 Subject: [PATCH 37/40] chore: update snaps and doc --- .../src/abis/__snapshots__/abis.test.ts.snap | 2 +- .../standard_indexed_tree.ts | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap index 71d1cdce74ab..b75a96b68961 100644 --- a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap +++ b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap @@ -653,7 +653,7 @@ Fr { } `; -exports[`abis computes public data tree index 1`] = ` +exports[`abis computes public data tree leaf slot 1`] = ` Fr { "asBigInt": 9076808949980998475110411569159266589807853958487763065147292518713994520820n, "asBuffer": { diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 1fa93cfd27cb..ff256dfcbf5a 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -460,7 +460,41 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * nextIdx 4 2 3 7 5 1 0 6 * nextVal 2 10 15 19 3 5 0 20 * - * TODO: this implementation will change once the zero value is changed from h(0,0,0). Changes incoming over the next sprint + * For leaves that allow updating the process is exactly the same. When a leaf is inserted that is already present, + * the low leaf will be the leaf that is being updated, and it'll get updated and an empty leaf will be inserted instead. + * For example: + * + * Initial state: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 0 0 0 0 0 0 0 + * value 0 0 0 0 0 0 0 0 + * nextIdx 0 0 0 0 0 0 0 0 + * nextSlot 0 0 0 0 0 0 0 0. + * + * + * Add new value 30:5: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 30 0 0 0 0 0 0 + * value 0 5 0 0 0 0 0 0 + * nextIdx 1 0 0 0 0 0 0 0 + * nextSlot 30 0 0 0 0 0 0 0. + * + * + * Update the value of 30 to 10 (insert 30:10): + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * slot 0 30 0 0 0 0 0 0 + * value 0 10 0 0 0 0 0 0 + * nextIdx 1 0 0 0 0 0 0 0 + * nextSlot 30 0 0 0 0 0 0 0. + * + * The low leaf is 30, so we update it to 10, and insert an empty leaf at index 2. + * * @param leaves - Values to insert into the tree. * @param subtreeHeight - Height of the subtree. * @returns The data for the leaves to be updated when inserting the new ones. From f91c762663cac83e4055e8407247772ed8b23f28 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 13:14:37 +0000 Subject: [PATCH 38/40] chore: docs --- yarn-project/noir-protocol-circuits/src/type_conversion.ts | 6 +++--- .../src/block_builder/solo_block_builder.ts | 6 ++++-- .../sequencer-client/src/sequencer/public_processor.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index e3521bff7cf6..a091ac685329 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -1324,7 +1324,7 @@ export function mapNullifierMembershipWitnessToNoir( } /** - * + * Maps a membership witness of the public data tree to noir. */ export function mapPublicDataMembershipWitnessToNoir( membershipWitness: MembershipWitness, @@ -1350,7 +1350,7 @@ export function mapArchiveRootMembershipWitnessToNoir( } /** - * + * Maps a leaf of the public data tree to noir. */ export function mapPublicDataTreeLeafToNoir(leaf: PublicDataTreeLeaf): PublicDataTreeLeafNoir { return { @@ -1360,7 +1360,7 @@ export function mapPublicDataTreeLeafToNoir(leaf: PublicDataTreeLeaf): PublicDat } /** - * + * Maps a leaf preimage of the public data tree to noir. */ export function mapPublicDataTreePreimageToNoir(preimage: PublicDataTreeLeafPreimage): PublicDataTreeLeafPreimageNoir { return { diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index 03938a997d34..dee982a9ba0b 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -581,7 +581,7 @@ export class SoloBlockBuilder implements BlockBuilder { const { lowLeavesWitnessData, newSubtreeSiblingPath, sortedNewLeaves, sortedNewLeavesIndexes } = await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - // TODO remove oldValue from update requests, it's not needed + // TODO(#3675) remove oldValue from update requests tx.data.end.publicDataUpdateRequests.map(updateRequest => { return new PublicDataTreeLeaf(updateRequest.leafSlot, updateRequest.newValue).toBuffer(); }), @@ -701,7 +701,9 @@ export class SoloBlockBuilder implements BlockBuilder { await this.db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, newCommitments); - // TODO explain this + // The public data tree will be updated serially, first with the left TX and then with the right TX. + // The read witnesses for a given TX should be generated before the writes of the same TX are applied. + // All reads that refer to writes in the same tx are transient and can be simplified out. const leftPublicDataReadsInfo = await this.getPublicDataReadsInfo(left); const leftPublicDataUpdateRequestInfo = await this.processPublicDataUpdateRequests(left); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 63d221af2061..333b6b83bcd8 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -217,7 +217,7 @@ export class PublicProcessor { this.patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!); } - // TODO: This should be done in a public kernel reset circuit + // TODO(#3675): This should be done in a public kernel circuit this.removeRedundantPublicDataWrites(kernelOutput); return [kernelOutput, kernelProof, newUnencryptedFunctionLogs]; From 1342ca50ea2c3472fd553de43858c7d914d9d0a5 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 13:24:47 +0000 Subject: [PATCH 39/40] test: re enable solidity test --- l1-contracts/test/Decoder.t.sol | 2 +- l1-contracts/test/Rollup.t.sol | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/l1-contracts/test/Decoder.t.sol b/l1-contracts/test/Decoder.t.sol index 0a01e0431c8d..c353bd517563 100644 --- a/l1-contracts/test/Decoder.t.sol +++ b/l1-contracts/test/Decoder.t.sol @@ -29,7 +29,7 @@ contract DecoderTest is Test { hex"0000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000020efbe2c7b675f26ab71689279908bbab33a6963e7e0dcb80e4c46583d094113000000002adc67712c2f7afc4e827551236adb46a693d572fbb29f3993dbedbef8d2d87d0000002027378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000002b72136df9bc7dc9cbfe6b84ec743e8e1d73dd93aecfa79f18afb86be977d3eb27378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000000103babeb39cf7cb3fd320ec225367ac5e834aaec3e7b48bb3239d0da39fa4a20000000120efbe2c7b675f26ab71689279908bbab33a6963e7e0dcb80e4c46583d094113000000402adc67712c2f7afc4e827551236adb46a693d572fbb29f3993dbedbef8d2d87d0000006027378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000042b72136df9bc7dc9cbfe6b84ec743e8e1d73dd93aecfa79f18afb86be977d3eb27378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda5410000001019133ced5bd7348dae0f82a7195910ca141c7b5c1894a7c5f9fa53290c79f3ed0000000200000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000001000000000000000000000000000000000"; bytes internal block_mixed_1 = - hex"0000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000020efbe2c7b675f26ab71689279908bbab33a6963e7e0dcb80e4c46583d094113000000002adc67712c2f7afc4e827551236adb46a693d572fbb29f3993dbedbef8d2d87d0000002027378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000002b72136df9bc7dc9cbfe6b84ec743e8e1d73dd93aecfa79f18afb86be977d3eb27378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000000103babeb39cf7cb3fd320ec225367ac5e834aaec3e7b48bb3239d0da39fa4a200000001235f4e41a2440aa28f9ac14e7eed447ddd2e539179cee4c0941e6e408d2443e50000004026f32989eb2870f2ed00774f54a82e8266fc2a2b3392b64e8199aacac71aabea000000600b6abbab461cfb072b267bfc1ecf8dc3c943736341baf11c5c829e345c49b5090000000429c0d0effa4242b8d2e372cfcdfa8bb57160715fa3640a3404a9bba93725a1072fbbd267a1c9b23b3ac1609b2c2a7961a0338c56d62607d5f8d6b6a29e7fe4cb000000102d81b3e2140328e322ad47f083bfb89d61aa26a26795d34e7442006f939663b300000002000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000012100000000000000000000000000000000000000000000000000000000000001220000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000001250000000000000000000000000000000000000000000000000000000000000126000000000000000000000000000000000000000000000000000000000000012700000000000000000000000000000000000000000000000000000000000001280000000000000000000000000000000000000000000000000000000000000129000000000000000000000000000000000000000000000000000000000000012a000000000000000000000000000000000000000000000000000000000000012b000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000012d000000000000000000000000000000000000000000000000000000000000012e000000000000000000000000000000000000000000000000000000000000012f0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f000000400000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e0000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000052a0000000000000000000000000000000000000000000000000000000000000521000000000000000000000000000000000000000000000000000000000000052b0000000000000000000000000000000000000000000000000000000000000522000000000000000000000000000000000000000000000000000000000000052c0000000000000000000000000000000000000000000000000000000000000523000000000000000000000000000000000000000000000000000000000000052d0000000000000000000000000000000000000000000000000000000000000524000000000000000000000000000000000000000000000000000000000000052e0000000000000000000000000000000000000000000000000000000000000525000000000000000000000000000000000000000000000000000000000000052f00000000000000000000000000000000000000000000000000000000000005260000000000000000000000000000000000000000000000000000000000000530000000000000000000000000000000000000000000000000000000000000052700000000000000000000000000000000000000000000000000000000000005310000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f000000000000000000000000000000000000000000000000000000000000058600000000000000000000000000000000000000000000000000000000000005900000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000059100000008000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003210000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000361000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003810000000426fcb9639d15aabe6d792e23ab12fb9633046d4be6911a60d64471d7560d3f6809143b7d4943a3485115d37e7596938a16c91b6055f3837640d8c36b8303bb3c06fb5fb553496e5e0b48834087e036acf99d6d935dc2ebf43c82788cb5ed1c6a2f4bd77ac2bb5474d48c2856135d18168cd6f69f77143c60b3cc370319419dac0000000000000000000000000000000000000000000000000000000000001020212121212121212121212121212121212121212100000000000000000000000000000000000000000000000000000000000010404141414141414141414141414141414141414141000000000000000000000000000000000000000000000000000000000000106061616161616161616161616161616161616161610000000000000000000000000000000000000000000000000000000000001080818181818181818181818181818181818181818100000010151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d7839353703914c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a12806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e005f140c7c95624c8006774279a01ec1ea88617999e4fe6997b6576c4e1c7395a22048b96b586596bd740d0402e15f5577f7ceb5496b65aafc6d89d7c3b34924b0c3f2d50d16279970d682cada30bfa6b29bc0bac0ee2389f6a0444853eccaa932b2a60561da46a58569d71044a84c639e7f88429826e5622581536eb906d9cdd25a2c0a76f7da6924e10751c755227d2535f4ad258b984e78f9f452a853c52300e212d8e2069e4254d81af07744bcbb81121a38f0e2dbed69a523d3fbf85b75c287ca6f33aadbac2e4f058e05924c140d7895a6ed167caf804b710d2ae3ba62b1b51297b3ea37637af6bd56cf33425d95cc5c96e9c2ee3077322fbec86a0c7f32c15d2a888c6cc122e99478c92470a1311635142d82ad7ae67410beeef4ae31f0902ba2fb964922a4610bb18901f7b923885c1d034da5769a48203ae6f0206a92855e2c01ddb3d6553386b5580d681b8230fa4062948668f834f23e0636eaff70aaa64519aafdf4b040bd2f9836e76b9dc13cfec8065dcdf2834d786e06260d10000381000000e00000001bc00000090b1c43c1dd10fa62d46ac721c5529068d286a18a86e22787da7a222925e32a3148915e9b0ed2f8793823e9b4fc52cddfda38739e4a23cbae77fd06d498b9b8e6ea0911e8e23b97a17da9fa3777775672dd21198963bc0bbf6667d10365dd77172004b20053ff5fa88f214c7cfbb1501c5aa7c9bd36a08bcd318a70f71ef4a5837863bcc1e9a539584952bc17cb270e2d9000000907686f00e315ea807d51ecea805379caef8781be53347315fc9510f3e9db44f3e65862c9d964b0007dab956272d11e50d1338bcbaac47886108b376f34d207aa5e94ae1f2adbd06d15590d0cb7061bb79b839a791f90fdfef054e878748fad2123fbb481f7e1d9cfdab406f5e8093badb645fa2cb49203e1bf68f945ca21f5a03027dfe828e99ea4b6cf8ae2daae54bcf000000905510d0f861d5cd19326c399727f453486bdc7b85202ecd41f5fef58b5078e887ec5b91f4d39ddbe24e2233747a1e65e6737b58c0850e148f7e92cb21e947efb518882acf619244aca195b5dd44fe507cf4f7b1aa9386b7904b01769ffa8211b3ea6ab32f2c85a4d03c6266632b602c655f1dc01662d283f3fb700a734f14c580bfc7a5d2228ff1a4c7761708d9322f2e000001bc00000090c6d6caffce822bbedfa8b303833ce3bb9b0b3a79bfbfcbfd3b563f02298c592c4c7216a38ea06d6e9a1a75b8ce59730cc56c8488246b4d5261647058936277ea7ca9ec6df4bfaab432e580fb3ee6ad005ae36548d2fb1e7edc2f1228431bb1acc1e3210b4f8d5b68647dbee0bb7924fe2727a6669cfc46cb82fcdc1c77e3a0de61eb3f0a7da43811a067fcc07a5303300000009014237a82b5c531c5b22ebeb4f7af578d7998537fef0b53ee2fd725d07550da956a9ea3ffbdd09144a539b5fe593a138d39e93551ff290cc899b250940749ec14f745945d067438975a090af541be7ca68c7acbec35e623263cabd630ebd11d6caeefaa085a44d44810e70f8d6a631a8207ea4aff253f47613ca512bf04b63bad9dcc861f61d81ed0d7a88fe0747c6027000000908e8e062b17d6f040bf583520ad0d92e257880d51cd470ca10fd94d0e03815062d26f1169b1ebaaf9b5dde8a5638abb39043bd0e31d0939415ed88682e4b1d0e434f879f11542360439caf4db74632afce8378f2faf1316e528cf3a64d7e7e3a5fcf322938c266d48181fb71b956c6f66353469056cf26e8fd84c3ae60f72ffcf1f9deb3363b93f5ba1dda798c93f4ff5000001bc000000908cfb998afce6a15aa54c86f27370d5fa09e60944ffd9ba0cad5da227e2df3e678226f78094eac382cd0cfa9fa7dadf181c190002fde911048c253e2f05e8267af3129dd8be984d00e9ef152762ef161f93fc72ef1ac9bd0de640f6930a88f1fcfa9ad5bf5032505cb5c90ad5a7b22cd0b2c8d9af4f224955b28bed5900a852c9418d94f1a1e968afcae8d489895996a800000090579b53b18d0bd8e4db71db680804471f263423a4c57a5807696dae3a7d751e6dd8c1340ed4e0bb63e6e45b1fa36519d89f164f4ffaa41d391d4adeee0d02273595d6cfd41d0869677ddfe43cf8d4fc6e711d983bf9064485083edf3dfac78ceddd10474c1e07234f04ecbc50cb1bac7c30248d5bd9efae2bd72baad5d108f98de87b6ec6414d7581031cca18a7a1f88d000000903df50823b273945486cac84eae73e266121418fc5756bfc8ea6809d69410c76daee250d34b31a846e74ed1e9b87ad3dcfe92600d5bf24d620319554a8c67b0c2a76816d6bbd0ad08d83a89b121a4209e751b3e1d2ab0ad916fa673ab94ae1ce7c5367d77a7e5094a96e8c6d85575e34ebcf5ccddded1e82e329b0f5d0606f0010754789ee4e8be98adc2578c242d3eb2000001bc00000090fb7cd18168f47d0ddf433e52f0ea9f4f1d391b476226feae60b6258e7e9203983154c17120ef162fb83d858ed5d89acfb48b5b42ceeaf4cc45a3a2774814a2bc080b2a5d139aa7d33d54730e7c6b72b8c81475d99e430e914d609adba55d572d9fd9a2d0d3b55765aa106faac79fc50ade6e09d2fb18631a6e5028dd30a03fd78756608d3390e38358345734691e130400000090f354980488624491d43905213b4f502c0e0cf43c88bc840c0cbf1f103e74c47c3bd8bebf1453aee92c4ea82615fb9f4a50aad10dc69dabdb8ec7c8e518f55628f48a22867302d0aedbe01330ef940b87fcbe1522609073955a1f4e53b580d9fb5947abd430f3ada138bd38bfe014bf017d257cc3cdae7ad84837ad0aac1b8fc85d7f619247c58c4b84d96bb47d61447d000000909a34e935d1b48a64b74a4b7a130471c3197ca84342093096b7d7a5e57bf4aa9d89f286b4a27619861589c15f9ff4ac6277093e2bdb8f1418ce45c88b1b94d69c8fa5194ad9bbcf79de7ae6596d0f81fd1549667d7a97d3ad96e21fa687964b2d29ea50b7c0fff8bed8e3f5df7c0b7528130bc9443be97e2cd56bfae66b9f471572223673a7027b9df9c8275b7bf3cdcc000001bc0000009048deb9e0401c49c61deeea50914ae71c267a5056dcce4406b02478aead4616303a6d36685b3d23d995d2a3c31dfcd58a4925ae67251b98bc7a08521bbc80b850b7c268e8b796d8e4436e156fd1d9f906c508132999f3928da46a5829c3ef8636ce949c5ea263c6e0a23427bea71a8fe5cc35344d0e9d61b2891de94fd71fe97e23fdd6a1f28937cd6a4efa1e3282614900000090ef21002e4193e387593866f9568792747b9dc5353764bb3cfc9691068cd7ec63509277384c3e12130782690775bfd7598b30130d4797603c432bc9b5bea6a27f0c69b05374e731c220ff281450b2d4e126716e2bee5436d160b2f6731bf35c900639a6d7e65330b75439d3c7c6245f876e2950cc7fa970ad9ff16ebb6a0898525dbff3e00564cd209f260c2bc4f694910000009011f59e99b692f90fb8cb1a1724ea0cc8585330f95afbc3393e707c4d2a7703cd6ab9d975e56c7c1d10863b3b139958722f4af95cfda6159bca44175d9b5c6ac481caa2a86c6770abb6d6925a00213cea8455e87213b575da93ca24ee48e627145db559c962519911b1afb7a47af5ba0460a4a97ba00bd4bfe9f52f194c5e8ecbf6427cc4560227224f38c07b7e59a4ba000001bc00000090375f55f2a7cb6057626722e28363fabd45f30ea1babf12ffe3184303eb5788010d104812cc4db5d7d18114007d9f4314e64fd02acba6c5bbf83ccf2f35446b21a1e7955c55e6859fefe6f71f2e16ddf4ec1ed3c5178daf2c9e5613c3e9aae3201946fcdbeb0e8719b01a18a25905cca216f034c4f94ace0ee4bcfb8bf0b40ff0cdcd8f1238384e28df63c1ba65f91b540000009025e941529ddcb4ac5e93de566fac91cad1baa6dae9cda86477c95ce97c7fa81b6a3db36b657711ea0a1a7669a87ebb593bae554b66a058af1a21ecf1b2c5aecfa191f54733a32480d29a1ba026b6fc90b0ee4e718738ee68c2b34e774116f045f1138ed8c99ba88b98affe4806efa98b32435d368c6f34dde1fa09715624ac0611faf13adc4a8a6eef04646897ce61af00000090005b88cd11607a66bd9fdf06b8023ae16072df1d1a7d223a6d7d37049dbdd417043e11e1736c01af9098b6e30ae6e59e1882deae1521bd41b011c3f18fbb569d20e9af66508f743059537843cd1088ca526dc1f174a8018e467dc19af5cfb12d20d09664ecfafa7b1acf78c1396f0dd20f606f27813b004b81f347c313fcc77ceb5fc33758c4042e3bba26e6b4c9ac79000001bc0000009011e23f2328a3cdcb9652a6dc95eca90584fe46f92b70362a33f8374faf6c26f54650df91601eaf32e44ef840c5c3e0e5d02e51198bd57920a5c8b22aa96288c430ca5c462b8aab462c9222c36adb884bf5149978707ef563dad2ab59041332b569c79cf44e915c5aca22b75ae7218e996bc17d8409b0cfe5ea948fa2f95162b00069e3046acb65d5e245756709742b6f00000090193875fe2d757b5c35f529652a27b43f96d9866935e92806baa042413da52f6785a97ffa06741bf7f3a52031e8f67bc4ce40528f304d1e9ef6efa19575100fbe49f2147d7e0ec69f0f232c239fcce61a50addc45875278d5ac24e389521fca0c6a49eda5f3ca7f5cc7f1f9fad15332c9e6548f77cc0fbafb72413f6af7a44365b67dcef769105d6fb99e6acfcb8ec3d80000009060f4380337b2cf5647dc40b9d4d198ab5c8ce6317e4ca71b6b120612539ddc0de66f6b5a42257146ce190103849efa4dbc68903f1a32164258c81ce43caf8b53aa73519fc442f2f93cb9494fa19ba62817d116704233bce8a761a13221cfbead9bba235b6567045072eda0f4da79a5abdc640ca003cc218d3ad51fb6f5699ac03207e37e457d997288c90e043a4b49e1000001bc0000009027ec222ad55c14e1b629cba9e7b024ad8b045150f409c624f1b4b5772994e2f847ed4c6fc9ba0b887fdb370dfab6bc9a029a882d513e848d0a000e6a21d392081159e910e1824ca8336326c6fd32e62ee4fb91d3bcd1ea95316ccfd428235da330cf4e74853790219bc1d868b696ce9f289d416dd11908e410c00a7a2b0ae38ac6aee983af156f7ab445944bf3c78e8b00000090c7b511f4186d7293c83edbc937e81e2068ccb3253c10908e1dbb877098d3312ff806bf187b9521032fc91802a275c546310d3ede01710cadb7fef5c5ca3f188c900120b753a91fe69a4bd6ee2c39bed71c7ca4d6d5718ca20a3c83795545b169608e6248ef7b16e892c08bdfb2e261f85863b80f65540b1a68f1fd872fd2a9012f645a9177f656ab4bf297b5340983a500000090acba85789c0bd050a43cbd36f5b1c1e427f898fcee8c905e59e58b0218f7f652f7f947cfb947b52336439ec95ff4472481c871ca400dca0b90bd36ca63cc72e884736cae8a5ea449e383aab49c4cd6fab9c185f5b03b2bb9995436b1736674b1a1f8a9f4eb1102ad1fc406db1efa35e57d04cb2b69521ca599c548a3545805d44835b5dfde8b9039207128eba0be564d00000e00000001bc000000905bd444a8cba9ec75f404b87314bfd6020f57de5ccbe53d5fc09d4ab7d0523bad932cc48f4547c19d6fb6e5dfd506051992fcaf21fdfda48c0cbc1bac1a0db7bec9bebf7c40350a275594cdb6c0f5c94478f2d8ddb378ee5553051ba1014b4ef11c72588a4add965476d0da2f6d92e8c0745301d16b48600e1818114f9b02d82c8fe9ea5faf6a6033431267feb6788e3800000090c2cbadc76ca5c5af8dcf7288d4ef510b6114c120dfc23294df68240116291024a307f69ef01a033c11adac13172bbcbd83499538f279d4e6979184bdb3e9a71dce200df699cad843448318fc765621c6e762e8115488774101f7e3fab9e5c3119cea5b3c016fa19e2fd7686d7ab0bae4a036236ffb0cd9a1de8adeb1b624e150e917033d9a6aaaf2a93533d70007bdfc00000090328f9d32f22edfebdb68b5243666e71fe47a0739ad54826a2c9022076c8ca9f6e33deb528608d088e3e1d9a4222f1155caad9f3c4411bd779fe4e48a416cd6b30dd4dc5af43781989d1677d3c9422ee5687fef2d3f2de01d28800086c3cdddd4fab892dfc81ebdc8538f69c21137b4edb06e3b57ae6e75b684510c05225cfaf9e3ca6523fa821c3769d41a21fde5552e000001bc00000090fcbe73df4cbe4ce643bbee6919bf8ce36bb0083e91ddf443c4ee83374ceb33a79ff29e7155da16a7e1a4cc387b97981d6c38ca137a53c59b9e953d528df5e966da7e28dd7aa35b22cd75f86a920715f03dca7ef0f24bcb605e4be105a9b5d5f3fbe87f5d8bc8536011782da77732815fd863e08348fe2eb2c550f8bec4410d3850a79fd9cd2e37a1d244ba3184a99d2800000090859704b25ed494302d05adc7012684828a21a45b8aeb93df0a2d1ea91e312d66a2832a040bd2bcbbebaae7cb9dd3aa225d094a55977583142576a401ad449ed0593e7d9fd1cbc37ce036af276f565a038b63875d8090ccff076ac7495b4ff9631e86a6a0cdfa74e2f677750b1a1c280fb6ee6ff516163491aa744aad3a1c487cdab16dfdaec960b66ca812785ccf1f2a000000905726064d12c28f46856076461dd95fef270926eacd55074d64c998310a4798b699e8c864d863fa24c164036b379429ba81e7b9b52afda13d16cfcb231ff6e36812b16064122a163cd7bf935ca76ed6e275cd0e355aa41880a54c6f4fc9089e66837f477a5a0dda745ac9196f94fc8e1ec36eb8ea932eb6b4d737d6d005d0b8e3f299a2c02ac881de574900c89eea6339000001bc00000090bfee39eff3bae13492bf9ce976a56c09f166dbcd46ece93ab636914d02a7392596a2593c973fc5329d8dd03aed494ce37ba3a9a8d44108be1ff4238c414f2d8b9de63fc3adbe43edfc0bfcd0b2c85b9752eb49a51f36b995d20a3f7d80b35c30097ccc4fbe50cc4bf627e51771b5abcd43806c5d9dbce141c8c002bd56ea73d3cdfb523d1b6195ecdab57b427096227b000000900296ad69087cf87ef862839d9581a851861d600fa3e1e0dd4eea3cbebb18b868000356deed62fc7f1645f1a0b088aa169164b7f93b1ebd2e82485a677b57b68166d7896b4c65fb4808eceecca609f95546159f1d2b4ebf30862f18673f1d0248025bac6c928438e32c3b087abe01b8e827c811178866b40b4e3c14f8d1b6e89949ba757df7d18c21dd7a71fc5d4efe13000000900d33920df26da7d1d6b36377566e86e40a8015ecf91f88630ed533afafbf02a9aae149987f96ebb264eb582b1fb1aef2dbcaccc9efd52801c7d4d091b4c4a6e1378ddadfee155b09597a2ab3b1901780f0e606f6e1ffb02decffbc61f6ed141c282122b58b6ebd5807f3005d365eca15d2ce2c6ee04e53f0279af43b59017f3a5521ac893649bbf5e44cb9831879a7be000001bc00000090cc65151f7ad0a4bf1b0355424b24cc6eedba544cd71860bdd03a6855fc8242a77f21ccd3ee05d414c7cc8c8ac06108cb6102327bdb2a50dba83060c70b6cbcb31c817da3bade2a008761a0055374adbb54e0b549fe8ef201b83064196cd04bc0df1500b86ba47caf85b8bf64c01e93f27290ee8f0cfb6a5a53d9e6e2e092642df9d33d21139cceda22b76cfcdc0bbd3900000090378d2ba4c3e4b6d7f41c3e40cf644800141153017fb22ed45e0afc3f3f33f26da0a42071f04441b98a1af1bc41fc67f46ec17497f1ae5e10850c11452a284825a40d124958218ed6ab40f0babea0a827dba3a9e6efe5a9ebabbe59a1a3d64cad6ff4d706d2cad4c3f18964bc3afa143e8eff0b6469324ab2f417a4c1508b917a3e87c2f826a2ae81964f3a2c5af72d81000000906cbd3d7a20ae20388791281bbf4e67beaa96f258182d43044da9f1ebd6887b1b72da20bf46b67a23096b3f8509177b9c8dc77556cdbcc916b1ed02737ce1a8b34d3e74058494773916a1d1ab7f6658b09bf396ed9a2e70ec021178f9078dd57ca020c7a36b4e75548658ac642118f8c74062ad1169c47c2ff445433a50ff23623bae3a52b5a614fab34d12eaa314bb26000001bc000000906ea8321846cadbbbd83f29e437e2f68ed651e32a80b91c23fa9aff1116e3f7d084d238933ee634c640a369880b6eb7e1df8dd518e82546a53657a7aaf8f8e7ee8fdabb094a22378a14da612310e7dd6502aa80f268fd9bc57345c0d62c8b5b3dbd20c652445f67444074c186de015ef73221e6e865007e72082b14e9a6b03f76f5eeadfcbb705c1b03d6b983e8be0a28000000905219224f11bb74c2905c836b29a468148d41fe096400b1c4a5622c876d4fad2c161a13fbadb299cb0274f3ebb24f19f994a0fc82dff8523587cd1860b97bdf73a8a389a8b933a4711ab03bd77b28caabfdcfd2ab79cc27fe4e347d62b7acc92d385c206e4964e6252c4dffe60cc0bc6d9d0e265237941efe40116c02ed8dfa3805a63b6c998936e0a6e3d220bad3ee1300000090d07856ff8c6054302a935ee722eaf3cb1b9ec87e1c99ec4d5f05b82035d25abd2919636567dff299030db4df879a1e3da982747382e803240281fd86455d3e0ccec709c94a3226ad404ba79d6548a729a9c9ad21e1070e32154c4bb5665ea38ba1d7cf28598b3c115d50283af0a9f83da97a6571fabad795c7da119c7332ce25f686ad84f40b3ef6b08328bdb8e4733e000001bc00000090d3f985d89d0e61756a2139c09fd5502a712bf54663733ddf69cb1d3101d85c63924c625b12cd3bd36ada36864bc4443899d3c0735a5c2a29f86e8c8673952bd937cc39a6101ea05ea7e29d5ca473c8c05e3d3e37a9cb63fe19a38b2143155a2412b7feb1edd44b9686dc1179e04157e5965199f3d597190cc205e17333c995c32d5875f222e11759ccaca03613f713410000009094984b569bb94bad9099f29b90ef394dd454e2af7e3407a242f48bd08cd3fc111f49fbfc8093676a0a0f958a44b532e771e676fbcec59a5d965a98681e5460260ccd9ff9afa3638d92e4405d1f26776855ed9458d250c2854550f2e7bc3b2a7b1d2e49b3f08100ce724b420bd2f3de6a8078622e00fc4616bbf50da777426ddb2c47dc704cfcd6deb695156ca4a60e8000000090beee2b163d5d4a655689e8b0c30c8510d5c9e0fc68df0c94e9fa90d88a01d433523495024f2bf686d72323d34d8559aaacc1806e7bcb45b51cf697e3dce63ea0443807ae95619d8cbecc598e60b40ea544e93bb21b8d5862942774ebcf4ab1f56f42db3b75e3bcdc61e2c601b73286789f49ffd8b16788ce30dcd556aa35cbf32e1f628ef27cda313b1b779afa78372b000001bc00000090ca71834083c2c4a2c84e8192dec2d6f869bb192cbf4abd9a7f4fcd832d27875153abb7cc2da5eb8ccb6915b96a294cce6e99ba7632ef6757d7008b0bf0958d11e1bd49796e191a0c0c8417b4923da5c0779aaaed17ad3b4fad14065fd4203d73375e945b7792260c28893e89e3474970f80a2d5ea0f524f3efb5ffa8c2b895cbd32c5a77595c55d0b02133b97519f24600000090de3733625aa8d0451bbf8ceb0be90358fddd5863068f18505e05f9620da1bff7285b56740cf001d503bc05ff9e47c0769b45c5066c9f521cba91852d64db3748354ab1e9d12dac0284d0b63545918f00f4f1bc14c98760b9f02a666ae4721eb7b99254922bdc980ee12674075da138d68b385df945e3695c337af5ca590026d81bc5170366d9e1ee6e1a6b964782859a00000090b46b4bc458ff824b02d652b9e0e7d785589131d3359e69893c85b1606bf776de78b58044d0beb789dfaeb3104c8321ea76369c5db074097f126f8b0078dffd3ce005eb18a09cf43ac4f6f1f38dd58d1bdb1dd0dae627b5528d10adee3e1a822ca5c1aa8489c0f0e8ecdec41ad817ee12d534a241f7a6a13bb0ebe6a0e6d6349939669d5bdb654bb8f69d111a941af156000001bc0000009047099475fc1633753f715412724344352f1e4e82ce39b92b0d12b252413edde0e7c95f8b3b5ebbde7a57ba3da250bf51b2cc9c8c3f29e8b50d29ea0f31015879ca8bd47f6e092955c3c45f028b68350001831603834529b9e9625fee1b2a212e006b853c3ea4fc9124da15fd610a40aecc48728f1be8e8db55a32b26de66d1790e404c95710c76baeba3e5912a53768e00000090bcabda0ca6aea4f9d32bd42a74401684c5f46a968e48f394fb82b06ed1a36b0024f41ed47e948ddd2f96f0f9e2df0e7e61f67440a2b6e41c59059f2f563f27b21b4a7960c6f2238faf8d3bae1b31c1ed8974932b1a4ecec12e11772265bd9ded7b6751cdb0ace6d0db1055121a81f8f39b86d4e265c2bd82aa63fa52135b18c7089105d261e8a7147712b67a4f41cdcb00000090e02c17c4eacd5cab85df9268d835e0b3c1210fb114be217c839a764f1ac161bbdc2774a4fad9803a4c99315afc07b84c3796c7420f48c4a69ae5cbb3fd637a34ffb4ad7828fb81961d3bfcf6f52530d0b53f35adc9f88c96612123a23a011adb9c2b8e9acb53ee608ff642307de4ae094c859489acfe3c1d85119c4a786a1af5a3efafe2685aa4747cc63fdc84e9146500000e00000001bc00000090538a0441399b0eb4b37558ecf90d9bf9d67db754fda7d60fc3deb3c4e767bcc087f8c11ee7ee94353bb1a943dc8d148f97c3172f61121fab91a333f97129592b891c45de2c185d57c1548022605fed55bf47dbbe5470fbb7fdebc757800c35880c5ae70e372cc46272caceb0003fd6a46126a790e8d65baf954669f71bd817bc0e8c9b36e735963afcd3256ef4505b2400000090bcabc8093838df793a13c3cf44a1a70fff0569e2b1356a210fd55913b23068d1a98ea92dd3df55bd2755d97b09887edf8860b310a675eeec0ac1869aae6289c3e49560b8f4eb583bf40f6e21654a6b80e7af429fc7f038d95ecffba457ed949bbb7995158b2061f986643795c68b7967facb2cd028641daedf89779cf3fdfc90d12865fb851a5cf4eeabc7911d3d8f4a000000901f9dadab38f2ad1c8c0bf1b02c9bf4d584069a3b54a92ce07141c19e7bc975ea943719224e6f93fe69dfd882b6ecc94ec80a1da4189443cb04636e92f09f88adacea1ba91c31cfb9b7474ff1d58d63652f522adf727c82127393617c4b1530d8c961d41e378e3f5f266efc1bb0b7455d75148d6c79f98c01a5d5dafd3ba03f2639b4b45678ef60587bea187af77a11bb000001bc000000903442c44f4115857ef498fa03b90666e4b043398d13f0c2523a31c4ad92bebb6b6a3826f7f9158e1610939d54d9a3a041aa38d1192bf0dc797710a83799f48e470deeca42989c44b7622c4b99ad4922673f9ca98815da551a3d3e882643bdfb9cf0987abc462b859b433c1c7e1c9e96b4b4961302654f31326b8c6ab0b751d6f267c2adcad54b9058cc6fd5e9c8b70cd4000000906d69f738576233ec2328d6bd1ee76e6b40dab296dc3b214e02c6418e007ea20470acd84bd70c7ba5c24cb4360c8641f399a5b928077b3d11ceaaa73c11f7f37bd246208926b9c1b0e55abe71413511c4d0f04e811246a32542f7df6ac2a53254f1fcb9dd9716d85697cfe7afb069c65e9c03247da7195e9d39cc81f3dbc1a5b78e989c9e6d8956376e65a68b15be17340000009076c552a5d849a23c028a8df9458c56ef39d43d4e88b83cc86a7c5f93937b9c4db4f58496390d315c983284b9b68b5a546ec81ad4276d487ad1327ceb7b1f028887b1a7dd91fc6df07232e8371d78770a860f4890bef4d66018bc2dd5cf19c19da34b5efceb81804574f18011c9d52373d6d4996811bfa4ac2f947d90f5dcff96c9a8a4a407950c1de8c24bfff838971c000001bc0000009020c2f2ce0288f6e482241ead06ee7eb7b9f7d75f81c08b0da7b8d74fcf71c226add5b6db17fc64cdebd52bace97d20a655a05db3e73cfe16a0b88f11c049bb4a3b84ba4b50ccd39a9bde94edda541f5b44fda6a243468e74b9207fcd9d0385417891fe89f8c98a7ff77709e225c2039b6669e9879b5f595f52b194051ffa1a28f79973c4766e14da62a94b96483ac44200000090899898e94b501a777a9c12fb71a41e7697903ee2467966a5fe4ebb3024763e3d1ec29fee30c778929cb1a8a7265bd94d2dc67b1f6f6c8fea74ec2218ebcfaafb86f31426be18238178d23a165e671a30f3cb1b583f31555cb2be7e939aaa3a5b730c719fc73baccb83f69c07b26787d0107c7576dddc9dbd9d5fc7d0877f5150b27379c1d0b957180db2f528caadbc870000009057a22904be7a901c8cc3780b0277abfd02cc690199f208162df307f4168800bfbf2d997b9164cd2094a905d0d990cba8bebdfc6b4f30f740f811b44d203454d4364376c90912dc515f7752494649674edce66dc97a66dbb635ae57019ec8c46279e5373b6acf992a1a49edfdc3975f89d3185ad1b2de46ec88647230df94679cc356cb7abadd931aa6f294e34c3eed16000001bc0000009025609637221fe1306882993148602ceb784483a1151fed8e832fa18354016cdf71254410e38ac04184851ef76e6fcc5aba4e9a6c9e2638e41c7c5f8cee1b13cd3b0d17035b91ad04c63ee8a196ba537c26145c76a657e3d58ce9fed1d815a923033f7f3b4e866678d5060c1fbe338741b642162e9b7b8f9c47652521db9d99936c4d1ad38ee22f40bee9bc0f9ada6616000000901cdc562761ae6a5df3735ae6520eb47449dbd832f71ed2fc601aba142d20e1db7af47e209133de4a4874478df07071a4e0e373f4c2a61f0ef9036ac6b9f7dc2ca68a9b9d42d95caf623a8d1cb579b14e8b7d3fb8364f41bb945b63d933917fccec8de9746d5a295470a0c72ae94f47ed1a19445e70fdb586d9e6e7040bda1392fe9f3824c366512191767ddb708db86000000090f1a3c9017a4f4e312db8be2df5c2ffc914976a896d42ed510c47de5e437d6a4d7c61f893b78170690fad42b3f785fc77aec0a3cfc350fa0fb8c2d167b83f3d6fcb68e198633ab4538cd326e50c60d09f11cc14ddbf310e66c54021708c305bf57fa089950e565ae42169fbae159a661e68e641373df3cd38f455f3b8ab7d502d8187451fbe918b56a7c826e97ece64f4000001bc000000904576be0ee1fb3989c2b50c3c12eb035478869d0070c9522062b4d729485a9d6ca310cb28835f7611f4c44e8a92bbce8a7b52e229ce919a836b1942920d895205469b78da41f4862da83b05228c9728fcb1865b5b24002d2cbd16df45312f7dd1321943f1f7bb6aa432bdb84462069bcfc58650c1b4bdafd3992b5ba65657965656128fe027ce897b2051c9c697a1bcfd000000906898e921b3171d11bfa8902b63cfe050b43129c7395b6408415a18ad43c9543ed00cf3932f6023e999bc2ecb19853b3f20cf8cd5dbbe8514d9ea535dc4767cc9c9e88ff5bc664641e6fcf7d60b4cda224238e457073e702b2aa03c6d3dbb3ee797493a3afb2d079389e1548b761aa78929c4654c1dc1260d1db3cc989eab174cf2639838f844efcc4f648ed6b1c635f700000090fc9483293459d113eee0962ddc86156fd3cd45d05a0c16e07b4c0f3bfa660e3b069664717211a3cc3682945993548caa5b22df049a2f86238b3118ea877ecf9829afffab51135dfb66651aca3346892221585fe7b4c05b112fdfeee3134f6e8d3c7205e0de0a7d11fd4ba79ee612e840d6f859a4e3c746d122efd2e80755b46e4acf8c38d23f9271e101865e1ed823fb000001bc00000090c3cf4166479dca196f8f0246666941e3159f4e630223144a4e7a5a3f9f275c365e9191c212e2463a159e9de3155255f0f9c3b26c82f97c1d82601be3c51eb33752533cedfa1df5a434d37565da7aeb49f6fce23d4419aa3dc6c29478260e4254119cd7fbc9734d2ad321ab505d3b3c81eaa6e991c9a101e7250d71acadce5669281a3586b524e89064a42ebbcbf9f65400000090f32756a1d3fac1cfd947619de20dd66cb5a3bab4a4b1735d81e0b70deef25533bd6183cbe0b46ce35a4fa3ebc01b62302bb65d2a6db20233b500577296657e24768a1837c1eacd099834e69d3f878ce837dd31a7b8a8ed78e624a1810a7aba36f6ddb23c411b2ee5034a366c18bfaf3bf8e32e6d38361c9150f3c008825b8be8a3e5d177c80b2450c4feffbf29c8bce500000090c815b26c6e93de18db7c07eefdd72c77fc04eede275c87032768403812324fb8627805521f427e1fe5f881739df67d2f94fe358fddc4a971495b8ab18dfc8bcd83acc0117b65e15ece82916332c400424b9df1a8c6faf744d3d858cc63d5748ef8e927b7cb58c0129426a11afea0bb752eba612fb3f3bc43b1a2c627e7eeaf83e67f54683a0b4886990b67e07ce112b2000001bc0000009047403c11095d5b37db843ee4def2f8314b6f35746d30dbcf7095a8f5bb372ba1f385c21a8261210f8d0ac4c574f48bcff6f6f328bd938542b88fed261a7bde161f45913a788d2e418e9c8e432edfedd0147966d3bcaf0b98d0df08552403e92fffc01a0508721e1ad14df4306b4308dee4de010f603093bab5faed87c6cf60ba3171a00856f2ac3b06b699f7bfeb90f700000090af5b01631cce3055d7a9453decba284c3fdc06b0b71d98331cda285eea5206b3d2849aa349f8964e41cdeee6e5033639273d9a84fd20d695347a217822773890dced57219cc9acd1c866875d58939b7436db0eaaae1e44ed46ec19aa52c5f07139084a2ab1750e25c4ba379c4e683c08dbd954495bf6290e6edc61fdbe268142910e82c6aef3873100e3e7f40c4e856b00000090eb59cea068d5d6f4ee1d8115de3caa9ac0bc1ef647dab13a897080ee608c4ac251f4ef3fef30c12a7ac13eb02de4f2a37027b245dd9f2ea62465039a847c84c9ce2058930d1fa6d573866a018abf06350b09f9ad6e6fb0934122c64e7edc06690432f62faf63bf11f2881d3026ac9bd17d2143f7bd55cecd872e6fc98494f20c9f1d3c6436a4eff1baac096c0c5c882e000001bc000000907a519a60fe33f4b9382b5a67a538ebf6f53574dc1b2226f682bb87090ed48de9760c9b3e96ff6c87a8d2f2bcf4eabd7ae4d7f7859b10b3490561221bda35379a6196f07f7eec1be17be8abf819fa543655da0b0e224a2206197a464dfc7f0f812f194256ec8197aa776989f7f6b373a3b7a9fdbd40f85b203d63ea04bb972dbfcd15e5e7f1107e6270662260771da70e00000090bfa04272dd685cca97b73fe43df6f13aec32a46040e7a6495aed0c841aaeb495dc9428024abb90cb8309a9bc381a08428cbce5642ba0c18ba4cebf593102a05c62dffebddd38699b7d0f61d9f0114e3d7c713d65175a0aadbd4ad218a71bd8c9c9822dd7aeb4fc66994e2d31402cf81462f3ca7dc5df16a2dd15326f212096b22b30f2e8634327453802da76ac65615c00000090a479c98778c84ca2d9882cc42ef49d8441e855a2f54a43e045ded34978d82b0b1b0cd89ae968660d697b5873cb35dc91d8f463c2607d6f8848ce86a8c9eb3d3acb36d049915591056ab1b79aae00a5f568dcc3c48ae34c054ecdbfed0e96eae51e734386b262e62de543bc05d56251cad682b889103e58a536508821f847033a321142d8933ecc097dcefc5430fadb3a00000e00000001bc00000090b7968a955d2dfccf014999255b9ca6e954d7caf9a303bef2814245542a14bbfe846c946b03c54c945928d7c18b4a756d3636260d94f5d69ef6f93c523b2d80e2dfa6212f9fa3c82b083f3cc5d0a6a500837ecaff099978ac1229a823df5b11facb2eef513c1b277d704e51c28551814916d644e8818d8f971e1d80f2f735644be615e57885d8568b3efc13b4a9a1fb0a00000090a10b4b7824af15267906d782f4904863afba1c3d1ed581e99e32c6c493a7846e6f846c84286bf333ed45ae38e0510284cb164ccabd2fe49d5791a710b94c8df61b337fe569991dc692314e8eca72af12107c2d75080debacdbc1318c0a8c40b6cb600c366a733fecc7f76543c4dd73ca4b237ab3b176d98537532fe0382ffddf4e4426f5227b0a4ac756c9f6badb234e0000009085c8b49b001dd30b7018b2a4a90e8485c200113f9c3c7867cdf5a4cbfc8490f3b34a056c35be01b576f39d69a86e7f60c2f6f8f1ee445b96a4449114ea6ed1e8d03fbc50bfc658c43e8d71dde864b506380ad15dd48cf99dc7fd3c82f3f1e48bc4409ae2ace69c4350fb1235f7e445b1f41e96477784299439dd984b19d8bba4489aeb66862d543ff340073d046c80f9000001bc000000908e9cc39a7bdf0721646ba3b0233c7b8631903679258eb14f27966ecc188d74c92fb176aed4fb55e0022bc417a1976da6b1e9ef8a26a9ebacc234bd030c9f930e09fadb0e5989f964ac2db2e8c08fea5ce219ee1c757de3b3305d537c5acbdb7164e6c0c5bdea99ca6e05751c3d6c8ca4abfd29f3027cd21467dda03574adf18be31acfb55227f08291fc7ff94aafa82900000090d7ea79f2a79ac7b7199608412594e9c39d6d200c865056541c65d00218430bb6eecca91752dd462ed04bf2337c17763fc18326195672e7664138f28f44afe7bd8ee5e36e5237fe193ef4c82c8d0c608bc078dec1bf9a6e693113e6d91e261c579c66478ac5a1596ccb91ed884dcf88ff8b8ed010c46d26b560d54b96eddd6b4f435b2149aca948f24d9403096efc7d7000000090cf657fdb9a0dd2b397c3c6f86cabe903860f881fa3d787e00179173c8061a7a903389c4f0ff1bac518ddccf514eefc9814f49f54ab22f25cf0bafeb92589108ffcd6c35ead1950640949e8f7025f5e3e669dd8f298498655d4f22d4908c40f6a1c9d02ff790d397440b7d0e0e64b2eb03b52be821ee39dad03788d613b876fca3053ce1fbbf61588be94342740011cf6000001bc00000090db066dd99708e02662124ea437c46cad412577c6b6e94d535f1bc556be4d2215c19428926ffe706f811af6eb239dacdcfe6f2b00c14c70ccbf2548cd3eb3c59ee030c1a0b3f02e952e3e370f39a3407711a393569c9905fe0be6048cada151dd5fb1337d334b5dc1c2b018eaf892d3edf265f5c22e45dcf843b0647159f4dcde84bdf31133bbf8b7c692b7d24c1a063d000000907bd9616b869597f8c331a4112d51429c804370966e033be8135121beb7a4dfc21ecdc37facdc01ecc6d1db6e9435ecb4833f197b6577403d57d2c09f511e496da938ea4898bbbeefcf5c776112961888d2aab79a214ed7e45d34a7699ad5523e02ae32c96a2a49c0cc5da2581e81ccf55439f4f5d0e52f5542c86a65dc8c30aaa326972fc852c514e57877fc1632a10100000090db2a0f48ef0ee0a981673c0fc77b364c4d5b02498e71853c6806e66f215613b266da6f2c3295f6a61099d72f94ceef9a0ceff8464c2b9b106bf36bf6ccfd512f56e7d026e7f680ae933978974704a03b27a1ce316c14c50bd191380b7ae6e5eed923737aebcf01092047f81ecd47c9441536c5a4579dbb32a1fa3c2d5758014750efec3a27c52ea30b68f5d9f4850e2e000001bc0000009028a58a7daf03e720795deb55e471a44b048b694f524903dd1aadc6779f6b286caaafcfd0b95d213eff8ebdf24a227ee8a2503d9c02ee079bee47cc032af93c1f513da5c754599e5c14a53cce674e1e37980840e2cbfa446d7c8e00ac0797cc6973c723f3e7e1e4f05eeb27463c1cac066661b441c36d43d223effcbb1a7e16eb9a616c23efa8643703a519fce0e3dec900000090e12682942a9cb9d8d97e0d8542d4b3eb2f61e1c6b18d15dbe382472cb8eb601c2191b239f98b6de85ff2d96b53c5158e33dd6d195bf4837dc481d5c068136ec4d31923c522342c81ff3fac8721a1da2e83d81be1e3d657a9d6ea592a634ffd4385226199dadadd8af222616ddd36b8a58b6b014d8659e76e99a4447a1773869f57c4ff69075ccddfa46de2db7673102a0000009014acf34b96562f756f3a34419fc01eefef5123364d56c35c3226813f56b7868e300d8c299ef09be1b08b38a85f759bce2b14fffcf6892f37d18c54ba9516a85fe9ed9de7293d7e7dd0a8d79ec9b069faf44f250c4c5f748b396901228c7bafcd7c89059fc0b330b37a2b9be7c4805c50c8cdf439e0f6b8fb7e8706ed29940bf7098edde8b3f427405865d2a6dedbd040000001bc0000009076e1ab6c68777495c22513e219f4a9ef4ddff594c95ad42337177a1fb4842ee0494168c135904a0820e65e95ef99548b80dacc87fe0ad7a58c67076640d3075e9f9f1cfcecce95b7d1178fbf6ae8662ac3e09c6470fb6ee972e1af3833309657eb02185552813b4231fb0dfef0046567243efe8bf958502aa87c2b08463f71ca4194bff89dd4b29b7e039784145d5e430000009041331715e1cb585124b0dc39418125664fe058a973203758019fa2ef0ea59c7bd5699c0d072e61cfade043c16f98fe730b4c8b62c66aeca37cb5914337462d7a8865ea805c3a989f184a154b8cb49305d9b15d98703e9bc55b33d3fdf0ac4cf476ed841c65a6372744f4edd93b550e48b49c3acbeea01235449807c8bd5edc26433d94016700d07365769ecdce72bf4400000090c362e0f4934778f4585405e6402fb9de60210e5f1209f942cf428510da7f03774f0121a7259defeb3f1ff6895b5dabb8fa7dcf1c7e1c8f157c01f00f62968a872d0b1d5d072d37aa0bd644d63489d0fedb5eed785b537c852f81b1def1b4dc788eb6d8255e396854a06fbabe3cc4482301c38923ad0be4f9696227eb3f5d83e99f554f1dbd79d1b64a50341a85a73740000001bc00000090a6e13c2eb058853e0865e0d942ec7d63cbca97e6aace8d62edb8429a5a2cbd2a2ce68508e01326c60881c584e71f4eecce64d2d2ad804c62ca47c5a0d1958b6bc7983aae29d9430914e4c09247d53fc8a1ecaa20b93662f4020cf7331a55701ee9fa57b97c9044d7048dbe7d1a9a350c96e0e22b1d808e8d24f17e233e0c864fddfdc7b68a8877c73cc191eca7eb9f1000000090922b263725ba81ccd8d7422243bbb9806961a47e96da934a3a5fc4603e5d5e4e6bc7d38fd31c84aa1747287d9f729869ab9c0f32bde16677534a83148f9c489920d3b29f6cf68324c2e5d864932dfa60fc97cbcd4d9657281712b022e29f5f89992cca034a6342d635eba4c0ad2df5af89904e4276ab1da6a39432ee1c0f9a8bf0ee4eacfa26a54d1a22a3d63732744a00000090c673de086083e4f2deaa6a3f34a917e374d1a9c2577ab6a85ff9bb583bbe7fa923f3efcf68348028587ee4b7be6df0a97c6af81b93af58038760a59f2d91bdd3126fdb44c80b208862924a53122a75d0d0a70b5433714b1eb1203cbf165a934a6568c68a22d358490deecff0558ccdb30e006a98cf07610a55194893c9649b30398a3301e8ba5d721279caa106549b6f000001bc000000904a4029e9184666e191b436b44bfa3a8857c4335075eb18bf413257765e02e3e16fcc66d3c2c32e339c0ae1b4f99a66232adfaf33417d71e3fb35cfa8272ab18257d412ae7c56b7c65e1dc1c4ffd6ea5bbc3f16dd46d111c38999b5d4ad141910dd482765f3cdf7d6fda72159ecf246f4ec5e09abf857d3627a4d90553dde7ea3f5663a02b08ce0069b78cb5e4f49c0d8000000902178902bc93e04b3f3a6658a3ee3cd0aef2a16aaed6f0249aef5a026de6f516849832b547d9a4e026ced2da2ede5ab517adadd045cc698446827bd8bca4491e2521ad944320d7198e23ae65432a90c51c7d0c0c6dd2bb78ec5a47c241ab318810f37025a301961be34aa75889f13fb67f39c272222d68fdbdf47e0ff5b3673b290951c674a463c68ca3105bcfde34ea600000090688c993c3b2a470cc31c4bee959a98d1af8c12d21e520b50773fd7723d9d9d6cddb095a99236d054ce3c11c6f557cfdc79bca98c54964cd68944b28f65831eb3ec6086b226077d5512c782e030b57e3c3276da66d8eb3af1b7d2d76a2a382a1b6e7f71e1c4130bd24b864dba04727ced8abfff56ad35533c1fc7eab7ef08949d08c5208ae3e042e763322cbbb0c65776000001bc00000090d50be3ce4e58c66a007f2716226fd00515d21be6161efc71317f535c0ad3f1cb0497aa0c55e3bec7a34675504334497b6958783f1090b88ee174e107759ae3706ef200b69e4e9681a67239f9512c4e86b101df5bf73f08d3fd08acef16bc9857b722a6472a6dff98baacace6f3478fa363e9500406db02772c4fd328f7bc82b0448cea431a089f6f567410d44992c3d100000090a2f72607f0f9161f24cc9c00ba8914937e24cc3d20d3b14c751076ed5481930c339928038c366f5fa1db32c055782c94b44d7e6a73c72b8370740c9a91972eb71165516107307e18f0d58c0dd51fb0585f05e6b64a7903f7e427ba93bb69a705fc464095757f14c5b86bab8352b84a0066210a190d0ff68fb88794730e6d63cd8ef03f632f8a6d28e7deb8a9df7dc83c00000090b5abd77435f66a78e2de4cdc07f5326e6ed20a594ebb0b3dbecbebcddb6ea6fd756b4393b024cff5cc9a178ae19f5088f6130f37117b37bd1bb5847f352862590f5a21dd0b1cd4060af8dbbb7300557e12bbd00fcbf953b4111477c5add912c77cccfe78306b3f29a5186bf4f005d948b3eef381081e2a8693299484d28445e4843b48c6b52582d8a2b5d84c749ac950000033a000000ce40000012800000090229388b3a431be6b9653aff04b9359a8608767dc2de0419efb0b91f247a4269f45a4b1e2a8bc2e6dda32f707eecd34cb3366b36803954fdbb8f279d852b37b33decc47345388d527433dba1449c00d03a490e6b5f7f0e9ad28301d099ac3492489f8287165f5c231389123b86db7baae55731f7d2ca674378556f89548ab1f9f037b5c982c7b7ebaa74ae94e2dc4732b000000903aee5e522b5f56e536df8ef54bd19f1c5bb1d820658fffd764da995e584a6efaf04d744beeb6127f19410af6fb65edd2261dbce88a18cf55d47a07c5c550ebd1d6425136927d40bc78a13c45da9e88993ef764aee2a53ea05dca3ed57368eda2c5a6a13b08687360271d05d835b94b78efe6b60f2839355849495a363ded3507648476e19e3cc1d7903191e5bd8598a60000012800000090505af5b4991216986ea5634f906556df448f90ee8d7c400a6e96e2d2014d9ca502059dbc82a4eb40e6422cc17d2c59399035a13cf18494af6e79de74dc243f69a18355d4ce14c1121ec65ec9722c4667e31ade9bb4ab5c41db9ebac1f0e215dc5236dd747e0ba8685ccaa12760865546d52a9e2245d699c8e88d89cd183e04c0ef8b4b6547141d247937a6da6ae338e50000009094439dac22b0f4e1a1cf2747ff890355cdb876d1133463d0f3016734159d099540628b8a81562afd699b1bcbc2885ef8feeed751536d9bd8cf046a0bdf0398984146839da849ec6bd897350bda300f04276a1b7d9abf799776a3c64294b6e4c9c768e19f0ef758e0fa97a706e6315cdc7a2370f89e301ccfb5664a9cc5becdf9bb0fd015936b984c9b4800e62b2e1f6f0000012800000090e0698c6a57315526b74f6cb3617711e0c054b0488cd2b3d413e18674be991c4ce9d6796564eaebb8977b5ae75ba9a47259348b64e8330754cc5543c3b867da1709fdda0a509cf7a216c6c8693547cb4454d17181ad1e67c51da2446c1b922c87836775ec86ed5aba1e4e14bac831021dde12ebd15223efab74e73b47d7d42a64b255dbe1929a91438ee5d82ec2c2b60d0000009072b35250299df92ec033153bd0799eae0234158b942abf7c23df05e6c60809a5e0a344d7de0b739c3c478c00c4c020c9cade6a7f3c10031c47ad3248bc56b47ac7ec58f071aabb500370c990804be96157e63e41ef9476a965467cf588f77f2bb420477512ff881df4bc89628bf8be5b095170733398ffb463ad6347e4ec5d3345e35be599a28d5e744efb625ba90ef400000128000000903c56b6d74c83e54735baca8111b0c3e5496c7cc3737c1cbea76e13761c0c086366de8290722cada540d19e8d45235e49469bfeab2b09f84607d6bc069409fc29d6ff6986e715caf2b411343e1b4e66c0aaec16ff15335f3030803585f8769fdaff07f9bb95d5b3a1f25a3d5bdc8b8420d6ab8d8c8c3146e82e330abf83ad8bb8e8153ed842cb780af11f4a7104a628e000000090d88f4ce50221ca7385fb78a7db3e639413b03b803b6bb177f100c95007507af701f9b4982607615066f02f598ed06b6f98be07c33e8283eae9483624822dcf36400e8193dfc720d12e58d57bd1eb0fdaaa014be61644f62ad2401ff2460f91b9a852ecd0c761ed2fbf6c00e721cea4f4387bc825f1bb92d29dd01e7aa31f4aba59a2188d3c2590b52cfdd921d7e8e57c0000012800000090aab78c0b3962855a0dce967579343c0137331b1cd622dc347ba3b6e3d66ac75c602484fb7a24e771f9e1b3f9edf768b86614c5e12f5aab14023a1f35e7693c048468d51e1ad2f71bbe06deb0a6f25b348372c25388d1cd7e44fb2a01194786bd2661abf38e323be07ac7e7c869348b6a0fe03fe712c3c2ba944353e7c161715aa4f5a6988a0531bdb757a5b96bdba738000000903dcdb5cead4dbc2d6f7f4dd0093b1c14f44876543bf016543b15a05938d6fb66f17f02a0e5467532f8441e0699e20acd7060a2b6ebbeb04f65af86b416b9a081a5cee331691a934379450b2bb4ceb4b1cab8f5fc861ac3d2acbb6485e2e689c91e09052ff61f9a819bfbea097cda0c6474f8a788a00cca8d4c863738bed83bc8e16e0e9759f9ac190d2b28987ea4231200000128000000903cdefcfd45b1ba2d83be0a46ab7dd242f7e5e895e7672f50add523a0eb6358f94c04302bf690ba420aa42a69e82a4f373f1887e7698ae51d11e258901baed21b6c0d6c3d16de591910540b9abad5c89a96bdeab09b27a752e94dcb2e71ed8571c963df9afa3d8d736b0621d941429139aae3da4fafa8def367250ad17cfaddf758914fd43d6901a92473fbb27726668900000090985b0016d8588b238dfd905cd864cae91d3ef4474833887e1a0119f5b8e67441d409af7bf106a2a9a10b887bf8edaa4d240f2de9cd0d7a180819412f076d2502431195001c2cb2b624c249519933e40cf7918e9d8856a8bd927f761bab2a4d2bb1a8292f4d63107f806f7fb971d4e3dbfacf5798444c335bd737a5e00c78f148c754f8d761c3ff396af00e6808fd01f0000001280000009001ab2f2f96fcaf3ee139547dfcb20712ace6ae915f8d86d2aeb96a8efc356a2b0c9e40fec9b001f22ebc49c53056f781aa0145115822ed88c143f12a353d5f0470fbba36e85ef4491c85c310feaa6a20093eaa78eb1e49c815cca3fbcfa32a942f90dd82ff94fdb74d66a089fe8529fbbb6046b30533d144f6a537fe8fd734ce32bbcab3bc2019aef32f77692397b2e400000090ce9fbb834c6c927a11fac76cdfc44a00edcae62a5d8088092a2e7545a3b4a7d6d58566a30760b7b3f4e58fbfe571c4d5c72da8be7b7999a7c29c70a02b868ef5558b2b70b1cba583d27898bbd2d66c2923944c85d4fc77d36a5241780df91173f2bf13e9c814f68c1fe6b5d92d46f7c1b2bd1e69ed00017262f1d8b1fe795b498b920a9be2086eb2f00b8f652964039d00000128000000908860e1a69a9e2cbf88e05b93915ebc98e8b609f41781ac97fafe6c3e18e8c276e6faaa4b252c784a9b8c09e1caef1265e0f2c467d6272fdc889e539ef0aede3684937782e4825d9aecc975e09770420f04ba1333c84fee4dcc917d6ee0e567aa421f205839425a57597de409eecaa93d3a4fb262f9a99c14bfec03e1437b1108323ee30fae221f925397481c6dadcad200000090966fed22f972cdb59d1aa2e6c28c7643e2d1a2dcfd4636345c21e9d2c702dd6e9c97eea8fa2b4a5f2ce409f9706f1c4f7061ba0597dc1772c5252d3f5c0996e17fb44db31970424fa04c39e4f9dfe1df3a19ef6536cf3678704566cead85577908d2920d4b8bdc43c8154712f0e994deabb13034ec5db65ed7f1a0099cce611f0005bd6b2d2fb8168fb09b296d6d3de600000128000000901e24bf1b818df3b4f0c786e0f65346a46cbc6e32a4b3dd7354a3e49e45474fa4e90e150e00d9caf47ff6cf2ef7e5af39576ad7b7afd93a1271dd3e6cd08bafc90cfe66772ae41fcd3f6f184fc55fcb61ee464c44442f22a5d6ae4d8423fbb03d2ecc706b6043e1762d51df7c4f72e4bae329325f1b813714178ac415fac87060cea5fc7976804ea4d580b5a6a9f2bc320000009069200cddb6678b395690d22a92613469428b1d487c79be4450ceb95273f6b409c3dcfef361f1a0fa88209a31eb26b67cd96374df6bbe2b32dfcd867d00d8d5f4030e3091d200e4d95a1c0d511f8f37ba31378e1bae21b3125818a75f52fc89fadf49b10125af4c66631914efe6b68bb40d1d6701e52cf14587be5e697aba1f2427d67356e5a9c10f3c6454ed23af43f9000001280000009075bdd58d142542120664c3a621de692476f985546d6e644cc10fff950d99e39c3dc88d84dee487f947bbb0f3917c292f4276e9f87fa5724f0733daa0922f338c1cf4ad40224cf3ffa9e0ef29349903fceadf23ab67bdc42a6d97b73bb85352fb539252d59b6e7e86238327d630e0a4f6ec9d8931377580c4f6ba30f5187e4d59f60f1acd6fc96ecbbf94f13a92830790000000905ed953ddd64ef5fbfe5edb5fb182de41aa7a1fd5f9e85b6d5ea74cd8a7bdafaae31a434198d8841c3718a07ce17edceaa7e3fbfef7c0cc8aa893fc8115857754053d8c77e1b16cf1feb8e3d0860585641d188ea2c414831f3624ab59d835851f1bd033090662fcf445ba08f4876bf71590e5bc3c32ceb745088cb0f2ef46272da82ee42ff496bffc2ac37a20d2bacf4e00000128000000905a341b6ce0ede8f5f8f6e8d32e8022c11d0c68fcb7aced295e2ad1a83a9596187a46d257d062ab1c70721d5aa577dfa429081d6a355708cf37b903ca19764a3c0dab929758e5193aa8d12150c059339aec63bebd51c3092d43145cfb9d3c451dc39d6381650d37398521efabadbe3c1f182848defb521b96f8010d5ebb0d567cc1a35b1f92239a47976e2b32989ca7d30000009073ccdd690f4eafe2eead77e9e089ba7511cae490f3538e6c84934e714a4e9fe512908ad79cc5ba5e0fb79dbd9e583c32a9bf87ee48c96635a9a75be8c35194ea95499f3249d0a1bd568e081c6784f79a20467f0d81545aa21a7e38ecf6ef9d94dd9f5df93bf563720b644bda89f3c1e229db4cbe6eb0dfca5adcc805e1ab68da3cb812817251b02acb103aed40541a2b00000ce4000001280000009026bce16f9034980ede54249ed45fd623470304ac7d5c290037b743d785762c0178dde6a5b11b70f67ea2aaa4bdcad784ad51517c0aad8d0741ee9896cdfddf5efa8cb971bf13704a62ea56426c5b5b28dba5b9d7431f3b703d16ca928b14717ad847beff58bd00703d469b04ae8d5673c764459d7c70c487e2b8dc56921f3772fe890e19d46937246b6ecb53f4f50c5500000090f04587d4fab3a75048c04fa486941e4e874b19ad77ad780f6074d2384fefac7ce05c28ae2ad4bf50e0025bade67aae8770584ba9992b15db5533cf3b89fb64677adb54c862e350062e733f35edde61fc884a5293d24e20c4310964eafb3c5960d7d1def9a6f609e98b1022a692f0fb786d81df0d158fff49bd11a990c3122e2fb83684fe6b5106cfa2733c6fab9fb88b000001280000009035a7eeca40c8e2bba2ed2b757b8d6490f44e13e161354541e0075ce961822a4eb27b051457f9e88be8684213c8fcaa00ad2052fe2886e701cc24d8d6f7a4aa4b3597c7d85613edf04dfcada7d7c39da86b077d97620635b6dbd29824e0848357be256b504ff5652165c4822d4ad820ee2c79467b423f5d31adb633612b5e5b15777d6ccae55ec70d99a6c10c030cfe2a00000090aebaa1ebca372bd33e7852db4163446d886090bcd05d6e5355d11047e601463f4507859694c8e30d11c9d74acb02a5c7bd5eed8dac2d9f0e5d0281007233a02ea4c0ed665441aa57c0c7082b942d65b53e605ec7d3d89af83196c02996622bd3ebaa5d54b7d4da94727478e2987ccb4bfb81a153a99603930fd609be0e6be983e1fd59f539a91df3e9f1763142f9a6ce0000012800000090524c471a92b6d7165edf28ce0e54d66a6f61e270e338d4cf262c8cd180db7be85cec5365b6008feb5be52c99461328e1ebdf6893df6f9808a74e2cd5fc6c60919000a617cb9a4bcf84e01cd6404e7d5dac142e826e737dc8758b90c4dccaedab94f1cbfd7cf053d8fcae94bd309f201831e0edfc6ae8e30457e0c0371031220947c78fea955d4680fe39ae790b9e963e00000090d3bd94123ca96bff2dee2aa370efcd404e118f1180d79530dedaba3dc3154614e9915a8b0901b005c288f061f03c0c60566d357ef772387d8e61e8f4c2b8654c62c7cd23c3ac9c033d98892b1ea04ba5b4a9aca46eb7a128045a09269f9bd4fee8402b8be68dab85fd929afba6f29a7fe738be2ce0e94820e0f9c6518bee1b44679c9780f200b5c6261802b4a0be2b1c0000012800000090319c0d94159436bb81fcc81be0aaf9e3118a5955348108a8fca1f4a77806ec0462f1899c78170ff4176f5e97d5a8d2721e7bc03a8eb6dda0351850ba330a2b54cb7128286d9ad0d7b58c37d112b2536db4d6a86d66a3c8559d4cdc19caac191228d72367ed35f94d5d2dd7043ab074a7ae22ffb167347637c3ba2ad3a0c419d1181e67b061a1de2ac917f012999b1d0300000090065f2755859b1133d853b59bcc80bb73af1189664b3195d624861c07c9eb878cfa8687cfb82c4f851b0528473e27bb9af77059ae1c4e23a5f2372ff75857075a753a9c75875ae2129823424e66d5d78f84afd052d61b0a1b31a3dfe6cb1a806aa19a50d5b9ec157d2facb4f563abcb83cc3b6aac3dcc528c329b4919a355907b36cf2ee524d926ba87092509e3d808f70000012800000090fa2875f8be9eb0776642aa5d2bf2bc615e324ee515e2d4bdf81c09de95cb6ab3136288cc91a3e145bd70fc686df1754f7162baf30e724733a98258f91792da9bbc4264cca247c12d94e1fc2f2e45f9f931060c14c2462f6b0cfaa1a6286c03d3e30d81cf3c03f68af6d107fd42cca7438ece9749058fbc2328dd9bbe02f4f8d6d2309e6c3b0001af3cc932dc8bcc8ed200000090f50d7d32ab91cd8c3aff752e922844a0bfd5edef2f6bf8784360fd5b486e44288b07615b9d580f34a302222225ceb2f04d41339ec684e624163d4917bc8e42193fe14b0c1c88d68f88f9067ef677bcb76c752f326f2e536bf531c021e543fde42a5aa9e1c499bee05f6d78647b9d9d5ebbeafb53b8604767811f8e2ba029cb438e555810adec3f85ba2521675c2750440000012800000090d3e5f1f0b7463a85fb887c73b59cded50445fe4e890daeedf6a576380d2b3fa11c7518fb25af49490abbd6712cceb4305f3d43d61349be0c9b4c393a8a6bbf30f17dbc06c2103572b1b5cf22f86daf99eb76470d469fae219d26c1dbd533f47cbf1a3fdedbc55ae23dc2a789e51a8ab28eb2a0f292ed0c9f7253913199ccb2a910bb012a2ccf4630fefa169f661ad3650000009010599e417943025a7639267a1e18127c1cd439d05a1039a059f8ab49f9dcef63b890dabfa6096ec7fa0ddad844b29842ed3ec56bd6190c811da59fa70a07f9f5571dfa7217aa63717e7e6409ed81e37701851638904cc026911824d6b25c702fddc156b58f5f48ef411176330f9fae31236c67f30a1650158369ffef352e4ce3fc6a4d8cb5fa6ed9c5ad9ad3d119701a0000012800000090a389db0fd184113a4e4b46aa17388cbde640bfce3325cf61bd8ad7cdad47770eaa77abe2e690f52076a252505704b309cb8851cb69b24c95a87489136e6f73c0bfd49b94a4ef197d01d754d1ede85e4663e923dadbb4a4b806746f2d1ed9d992e06e2dd80f320f827db44059e1716ea92a1fb7c77e0d624d5adc773982b6c8d6687b699b3bdcd74edd00d78a69658c7c0000009028f5c952c4c3cae898a4269ac8ee0a0d4168e771a6f8d58561fed45c016c95c495c721923916f6d187b2523f7df94e7db8342aaa50f7a8c76823499ece6d3a53b1e095c4310c0225f60b20a91b86f1d2fa94c0ec508cc26b7b88b01a4b3b17e06698d586af6478253b877068f6a846f18fb0179240bb56e4e54770b3a4bc215c2be2b50673dbfe7bad40c07f63bbeff30000012800000090f2c814af6271db7c8e9d4e66cde9621bf22aef0134da41f47c35f71e471322fd66ebb39b40b974ca386bee6418469d5a1f3614758edb5ce1fa9636e03092329d8974e695dbf86e8da247feb4968a7b061c729f685b9da1d8b8635e04567bbd2b33eba0c833ee0f0bed056365873336e905c137ea0f17978259c2ea780c8c25ba328af85d9d98a0e3d938ae4107e42332000000906c52a4d740a9a815af7a7e95ba09c78226736edb78e8802d418b12022485c3b7ce79547cc3dee2c8001c0955d2601cf7aea7fcef6a09f1e36410a28a233be825bec542c9dbb53b6b2d203676d2c62b06d180b8fc42125ab058b9155a27244b61552af9f69f214acff4373f901e83c64a49116c2dc7b9b312b4fc93de2bd45a90864fbdf25a4ff286563a0ddcb50ecc770000012800000090bc8e92635d068e8f45cba5df8453691e130a9fafddea39c56f766fc987ee093e76ab21755ac24471f4bb049e3e71af69d9b12bacd520d6541f74909f903d3ad05830a6453d20c79fbcfde7ea423693eae7176bf537bbabdf309fb4e4178f40ac2eb048540dd302daf6deeb69aad9617361c56304a9037a66d39a94c487235c862b95d933caafaf0a964204eb00b2fcfe00000090f1d1f253bb1383ea73d63955d73b688b3daf0a019a1747ece001a07708988e629e5cd0d557e8f2f674db96e904a69762d1bc2eb02083b535e7767d14ad60fdebc1ef938d8422cbcbf9042ffc27a76523f81cf17bbb658d9cb951fe0bf9335520c00aed26cc37b7ac1eebd297ec02e90deb147a3aa5f2933973c37738cdaa040db84200c040225e7a4d412cee477998210000012800000090b864adaecd36d70266605faf87d3eec7205abd549042d44f60ffff66ba0fefe5f2426826b44c2779e144f28a162d06d24efe8cf29c337333c8ee42bdf40e0f5f836da60d7ae07495f821360f22afa06e85fe1ff0710fe0f12f2e1a053cccd9a89eb8d69b12d0604daf20a79e2b310d069746d271cfd788b448aa9c907b0be49faa548bfa0eda227aee965c8f4b0cfc1700000090cfc24b1d4dc86a7e0b31a4b03db181ed811ec14b156df7da4bf44ec6a19d7154ff5b7543d4a117176f0cb5a509706a19736b86ce8027dd3ba62ed0e1bf93bf2686201a6aa33bfeac26fe49c2a91dedd85a8d0323668373868b40e65e662edd3b079fda7b450524e89ff5987d8a5ede5117c005777c90a426862f76bfabd4ee3f254525478d37403866cc1b536b24519f00000128000000907340b3e06f362d0e25af7a0054eaceb640eadc92c26ce2adbd225e40075c48adeabba9a0286360795e5aaf7ea15560d1e9ccb9307b854729ef54170a63bc4584cc33744bac67b66475067b808c7b3612837efb49e00909b129456195dcce20256d5aa533a5ccf6c81644deb9226458b01fa3882da3ccb1cde0778a76e0ef1de5a397b1a74d1015da520af795974fb68300000090d2f4bebe950156f5a477fecae6f502f2e12d0a6df4029e8b4bae461d325db1d3b5812a5bb055406757f41621c76865b80d6be58a8bc5d9916c0627eee5928cbd9531e7977707dccc5c37a1a8a91f61395a77fa5fa5b7a70b8e677d311963474577abb368e9e3e571a3161f82c4603086ee0acf8c450ac2390b5e11e41c161ed9490c0605cef9916dd7ab4dfc8639aaba00000ce40000012800000090e549acf82a2d39497468fb5b925614c77bfaad29c7c2097eb12481b38da2cf526d83815bf1098dd63315c17a3cadc92ba6f513002cdf6a388f1de593f4b1075fcfb8ba1775f0b9796dcca84eac6eb940a7c45a9fa6c123681ca3d9e1879f61b842aea625a7de75502dc5af86d6b159b7cab7fdefa98b2db1345d07cf7da54dc5b1735ea31d2e75bfce4767273bce88cc000000905b57379b21c844b13cf8f3a037091cb3d0badbf3bde4c962d6b9c3c859e71295b1854091dd1c6668566ad727f9020ade69e19d34adcda46d2c93260a54929ee78d0f54cd90deeb0fc3d5860b7cf373b1012cd224e4bf27172a5741012cc83dd2ce8afbb70c32e3728fe900fe16284ae8b91f09055fc133f32923b06fa3f594635d85f964e8592be2175673eba3b07833000001280000009068833df188633bdd6038b3056a6f3c496a25391b1931b2f7cad08ee450a9a2a319f9781f9219511d9e739d99a6339a386822f561ffffd4d03d7fded85c2aa60202b8b67c104a85455a269fa8ffe4aa104e510b61731cea00eef07a943ea86867635706b6dbc509ee7e710247f254ce37315c9430368fe5e21cb5868b0f8c15d0494baa5aeb5c6b9a3b2985b3791b9a6e000000907676fe247936a75a03de9df952f34f0e82cdb40a1b057f932f6b913d30f708119d8857a1048511ea14e1710db2b90dabdc1351d9521f5f3064367a1d2db14fdb33bdd44e155cc39f61795f024ef460b307624c7a5e004dbea07c89cd271f921a21d225bc1cd87db49e398e1e8aedd923c9032f07b7e29cb1ceb462a59a6b2f9aae7cfee6064ac65578fe46c69cac75ca0000012800000090811ed27dc7ad47725b36f2c99ddb7e4cb66d6ddbaabe12556ab48b82678be51ba35a2c8401214e1c52e839a8a6903345b56c482977ab438bb64ecaeb5b6dc871e3a2876cf72333935d688ba754f397f3b96c795a3b292eee1f0def244ff2d3055cff950f727aef6d10ee069202325a8e154d526e08bd9dca4115276a99642612af60bb882d18903d261879dcb83c388a00000090e215b3deaf1f80a0450ca99498b82978e61011cfecd2b62ac1302b54f5abf8353658654997d2d66aef07ee1cb22bc33ac492274e548657b9d72c33e15883ce164fb56df05ab50d4cabc5ec4b3e328e98d595e36b1b778577cd9f07aa530d8049b4e39be4470cb7ae5afda18939496c85969fc1d3859992dbaf71a57ab515f4ce475bfbb014d1a3b005d698240f3569680000012800000090393392d7b7dc83078bd03116d5331cac8598a7d3dbfc13828d4a755de00f03b2ca88481407fbe04314601bb9001255aa51f43898cd2eaf0a1c3e11987b40fb4ce9d81f838de5c7d1ecbb79addcf3e27662abe805244c5b3d6fe5bd6e542eb329f48584e30d09d103679f52268d63abcaa6d02a0e9596b1c5f4d54c4fe11d8afb084494242cef2761e6be2b26c69cf28200000090082406fba8c9ae1b7c48b88bfcd627113f0d3f8c845665439fff90506e6d10257cdd5039c84c2b726a793c46fd7e30553bb33e2b4508d626e3d1deb2742683b26bd96abc118606f6fef1e0254203743f7ba5e122cc28a61e7c25550ee83cf7f363fd0ff7ba950447f08f868332b7369af8dea778aa4f2bdaf19a6ea422f16b9b4336b90d597c150712c3b083fb5dcb860000012800000090566d5d24fbeae58464b0615822f5dc1a5a03e6387afc0f89143738c3a7a82202b4d56c2bfc68175482f4cc3fdee16e023931a4dd760b719f0e31325a7198dacd88e32019efcadbb2a52b9701d23476fcfaff2b0f621845c3e246118f5c45e2a6790536c3984ae9a5984b938c3297920d7119a80e9e65dedb85f624888ee140d33fc9acd649e269a3ddb1f6bd31663220000000905b94546562731f1304d142db43c5462d32c17a7571b9a3fe2fd8b6c594b07a1bbbe4307e8b5c8c79213dde97f4a0ebaed453c4e798137e16b7d97c5882bf505ab2febbfd0a9bc802ea653874f11d6428cbee3a13a18eb54aa068cf2493512935163c59abc5d41900687c0222c084e85b476cbe9e2c1b367589a987f8c85c6b31257a22cec6af243663028f6d04b83955000001280000009047f97e0eac0aec21000ef407ba1cee999045431c0b8dd55027275e04502ab79ce4d0b6e5e86e8267a4e0234e029b335fe0a5a425638b5c07cdf37819afcd4b1f89246b02dbcd453bd7c2b0b25334f835468e6898a5c6066e62b8d869892dd294e05c83441841902532b4b5614c537bcfe557e329721078343e340bccf1abd74caf709ba0764352531308a7f5abbdf36000000090538969d6d762b76699c38f562873016b04d775021b4e9d2968430a2b3037c9cab3ae7814fd6dc7540e1a776bc4b75ebcab68a5fbc3eef095bbd0897517073639167327c8ce14e0e383c48475123e958c0c547ec4a2304df37901c05672f909770f02994c6f952eb5f21be1782e8f9b58df9967dbbf40306aa622ae9b4f8ef918105bfa6490e6311df30d9caed97e65180000012800000090ecf13baa64eae5d4ae2a2171abf58b0316e596ffb4e45d8b04fc6b3ef7baa3b6f144c69d47e57f4ce71725c1d23d904eab72f1e9558b38a2ec2e03490753746866158f8b5a1c1676b6c104795c2c72b46c02a0a24b09b0a167cf793e6d3a8fc4966487e6b9badec4505406c897064d16397c8f9c19e3be5bf2b6d836598a05aac481b44c2279db99967c7af13f9b754300000090ec3107180cd218bea3427fb271d8ee8dad175dcff2c80f1d9546beb9e3888f3da8632ce4672f5a4d826d219dee2704ac726a25cf699bc587432d1e76c5370cc090646ae5eccbdd2fd291b96c28f012c846308c58341f691de11365856500c6ac11ff5a20b28b39590fa03cb51730afcbb5651820568c5287dc99816279536f9016d8d770bfea03cc7dab39d4727b62910000012800000090b10f42fe05e1e1db07bac42949c8ba753f1c1185e27e061e5c73b8cf47eed8f356f38a8253891f322bca6bdd7d83933291953bfe0f225edeca42a4903bbdc73484d2b13347bb5b6ed7f48bb2215cb7b7cf968b29972e7ee078af4c634100613d21fd151fbe189aa19b395ee32bd671fd7bc3d1f9b8add290760f8cae20afbf91262267fb012b19527b11f94a35b5d8e20000009027517a0b48a659823f506f85de46054b9e586a0b9ef8de12a8d5f126d849333211c3e95f572cd162217fb7512eac7a87e0cb71a81c004a8d33d97e85832c1b7d33d615aa220cddc2ae5389bb239b8e29652b02e322d20c5e193bb3cad2efca03afb2d04c44815e56e2ac9caa2b68d32dd291ce3da9c7debb6646c0de58f4e321df762a3cc55cfc30425221a2f2cb26d10000012800000090471e0b99b2d33a1c6c46137b51404859c121b605ec6ba2d051cb05000ea2af1038fd2f55e333b12143126d8430e576a3102a4cd9b7d8b28bda0fd68c80f9f4907d273e8db07d35367eff9fc93d715a1e1e20c80b76593addecdab503c944603e1ace2d612f482eec9d23e9655552ad9b5d6805379b4ccc26195ad730f4a192cb4f85589fb9f4f0556480a78593348a5000000090b56978aa1d94ff2e2593864144defcc9491429a862274141a65225b58597d15b8eb13ef060cd75c33246df88241964c87eb08f2639f24048a0dd94bbca3b0fb4a6fb82916f2b6fbe1d05504f02fd6b4a67b58c58085864dbb6a26f7359a1e256e7fda12e9f4714dbab35fa1ff05081380bd351516c68b542f7068f2ecad24139b59055599b1cecae902973dd934007a400000128000000901ff8c6da2a57ce3580ed242c3571d9ec27f35ef436367a80bb500e65a97d49e48840fd757984eeee7ed4d7b675e2fa761378d270ceb582a96f4abbb7b90062747ab25a1e6fe505e7c3f2187f38c8996d773676380161053f7eb3f8c97387aa9c6aaaa30b97a546478668ac11ac2447f8845f1139820d679387fda7ae38627b95d2e1bc4a1ec6c50c02b6001ad67dd621000000901a0f38d26a55c352cacce5719aef19a35195aaa7a0660322ecef21d17cdcd4beb2e9fdf909522e68ead4974dbbc61ec727b7c192d649ba9402ef11bab69a4820c4ac8466678684a4561b79c607787a99d0ad4c9bcea79c0f6141417e2b2c08927e61c1412c6116fd383ce2b39f5284887477b1af8a5d4287b6e93cd9f6cc2b20bee631b52586254fca5b4eceadf0fc3b0000012800000090d454eb3e205fb713c0e1f57ac87f7486e283bf5373ab8dd7d6814b51e647c906668e53fdecf34d62c2577741f0326aa2770dbe8ddfa38feb113f296e8154da0bfb8744ffdc53fd9952fc2886e3c9ff4d9bec7fc40bf3b419f7cd8f9fdd94e5a80d858c55d3d8d6a78c4f38aafa2e57d344f3fa33066c02fcef95584f53fd18d77746707d7baf40f3d132e0762ff530da00000090447038e5cb74279855f930e8f7b7766db0fb5d9659e29f100a5edee666e93509b6304c0e244f64f622b85fe2edd61b7fea4129ee9877221583820e8624e32d92ab91f7f912589879ebe09ec84d6188f1afd24505c8d53576e2f4ceb6ec542df20ff8b21341611074c545fbb75dd8e4b9ace680bf1c22d34acd865dbe1ce75aa488b8c373d2601600ffdedd741f163ed500000ce4000001280000009045436ce2e6da0bbfec32c2a11b7de39d7dd557b2c44a87a827375716d3a0bfbf23e4e3027a036a17fe39610b53226ca60bc1a401c108e7a175a79639423d25e87c5bee44843faf52e06dfe9c5889e44696d84e60b00c808fc658cc1ba3a0380981f00a9b7e08844bbd7a30005ba8721b1933fb86fd11c4d51834fffcc1ab349f99bcda485368666b15f1e3668cc181f200000090bd229cf6d6f7d21631b59a23f3dcab0a9e2fd1bc8559a231695b6a5c26615528cf204d00046fb6fb2e67656576cbb6d72d194763c42e03a99b90950193d0d71c906007f18e3e1e3a7dc32ff538e16de9f4fb1ff8469289acb071318c67b9e99de9220af87f0936e67dc7dd73f06476d2c387fffde703d4375bc237c735801c71af114a06051621aead6dd13729d131990000012800000090b1095ccf7c01d057426cbf1f0b64a04461f96293107906aa032cecce60a80895415eaac9d968cf92b9bc23be2e4c28ad899b286877d856e516c45346416a54c7b851d3ee2310822c48ae58b6aa98d77214590692e544beffb4cd7db0b178bd4c73352d26295117d7fb3035e24c5a46375b0351068f8d13314c72c4791ae3ee6327bf1a69223068ebfbf3ee1162b05b3d00000090f9c41e0deba7b51b30183beee0fa118da199df57e7b64f7edabe514b9b054f84a8ac4a874b180c1173b428aede80f9065673f90cb90b50a75d45190411bca2802ec52d39a94964dc713f22e7a84e2b3951dc0147667b61153d4a7f05acc134188a20f930dbecaa1a6bf25d32efa1522651b47f92bd69e4c23a636a55111254efd05580201b50b964a1f32dc9025107a200000128000000903e89093271e6a69b711d07022e4e5e9c150a25620bd439d17551add63a0d8d884e45a3c44128ead9c2d3d8d2c3d4f92a39c8f93cb7578f1163966a8fbfc0e9c525c5d7f722a08d5c7352cb9a75b416603403f181652a7b06f8ca9600e23b4a965cdbd77ea9839d8e7e4d497c383e5e37682638f763ab3cead19ed526720b89d95366d5ff51b3f9c5ae658633f3e2c993000000906a4e1ed85756cd8c10680d71a1836879fac29cebf1d66a65cafca1ddb5b9ccf0c7eff289bdcce8b3dd23a1f904bb453359354d48579cc3fb3fbbf992a3f736154b6c8e1cb2e51c5e2c172cff4d5008dad02365cea9da1d63e00c042a9ba57e413c4a06d155fe9b9f86e262e97463597a82d7ef555baa2341426e0b7158829d50f9e4ecbe06444b6eeec16f96faf4550500000128000000903dc7fdc3162430d001f7977c0763f2ff678dcdf07c2f6a2485deeaf993ec94a2198d9ef36d7cd7f35081345641df105b6bd11a3491769f78c0d903ff1431a9e5dbe4018ab44cf0b9821ecb5ed32eb34ca80dffa898289893d917d9b1fefe265d6a7f8399c9daa180b1febcbc1c558d4746e5704d3405512a4fa1379925e58f33fa44731f43927a950a6c8babbf79a14700000090c596b4a820d0e7a8836b13d5e7f50bc9755865b70d1346e590ca091025a25e0add8c50137f8192b347f75bccce887e3f831d57d21252cab4b69dad4f17e1e0d280d147c23ab8bc57080c4812514ef05f63506743e267f89012fceb91c0688d6f5c434d4c5ff2ae87387e1a186b536e5bf6e98ed3291e5342ce9fa44c90cc5ba90387a4717d6dcb6e059145c1ce8764fa0000012800000090283d76ab2e5af53de1d18916e20f8b86bc6a20176941530f8619cc9cbc9fecc6a93b724f7fcee354a4fd6f195841ab11a75c89d33aeece8eec50e5fd4e546caabd8c04c6b1961a9fc37bc167fc31f3ebc1eb5d6dd77a1d04c733862b604dbfae14f6f9e69fb510ebf18bb64db753b0555b44b49f6fb37e41833c2cc1943c57394e0bd0dc3ce650c720e383a2de6f08fc000000900be60fd6de9a29388caf4f72b62772b80a503fd1449f8de61c72274e30ec13804f576b64345d7aedeff7af7d4b7ca95f03409036f473f35d5007585a2ae57ab94e234c195c07e0b5396f64211659a785e9a3ae4a697f5dca6a0a3ee2800208a107b12f40e5ec28cef8cd611add402aa2b64bf8938bcd1c2e4faebb54fa98cfae7b56b4a7797d5773a3b13808ebf2d3640000012800000090e1a2d111767837b0a5bd9ab4aa040837f01dc81fd1f3b4c94c577bc4334057561d356f081587fe288df0698fd3ca2d1be1fd4533a3d985a514d3b47a8f0e25c6e71b77a0f2a845a9292d016e16dea8c48597750ed24ec27955b749b8c647b1dad52191d4619762d90b395cd161201b5801587c46c623c9e9c85461a0998f13f32f3dabac687c6b37f0452793ec12029300000090339f31d769c14a357e7c98d6f5844e7c8a97ff122b25e0138d144d983c8219f971a67c20aebe049899c7ec09a3c7673ba857a507af1f5df87b53ce538385894b27c2e51f69ca75cb36656b09eaeccbffbea93c8af1dae1a5e8ab4c7c576c1f8521f27aae4d35c5bfb2868134d8dec61c60ebdb108b6f710c7e5ad7cf07026b0ab30df451dafb7d129075b4e2e29e87da0000012800000090446064bffca204655f9bcd32448d5281fc2ff2719cd87b3af607980fca826d2475cbb26176a5581312a871e1f3437f8b56e3e3424c050b0431daa7232ff0628d3c079dd7968229f7f4052650e2535473e346836f424308e4617e69233e04cdaeeb36f1952d6b42c278ae462f8c494d767a736ea0ca5eb4c7213413aa20a50b425b53e56c525fb4e4c9a4446ac4eddbb4000000908c5392f39dde50a945b2a726a59b76e677db424b16a2c17a7ae63c5b8a54db1a3ed78c150a97792402f186fbb8a6e093293ee00ff8b56cba7e772d89c79b87d48dcd17d436d11128b1f84ba989025fe5e25f731ab1fd99185783c13e2b180a344419a78577c541e4fda696c8463bff0b69cc80a23b3278f1149614cac7a019f43e484791680e168ea97e21cd5ec8bcc20000012800000090345b551d49681f065d63636d7c6e71cc73992b3b301f9591c577572b36b22e0fef0ac09ba7d7cfdd0f49a13739ef4314eb78de142191a75976123da2a49640249ccbc2af096e3900b4ca9bd4f09f52600c95f374ba110d6af27c2e1e4bc776af4577cf8cb55af20f521be2a0b9840a9e24c76aecbc0452ba6e07ff5762fd337ad72f9bebd508596940230d3a3648f54900000090ed47ff927b3a0d86080173815ca57523eb6dd5e261e587c375bd495e8bb376c15c14fe2f7086941bcee511a2845b43dce73c03193cb7f4069a18a228f310c87c2ea4c7023a863ebf36b3dd8f205b4cdf8bccfb99add2b6bb9b47057840ae0aad4147bc702c940e977f5be5fc96dbed0f578bd3c8fb05e73a83eca17aab10ff63f7781d4f42164a7ade252a819c7d8c0e0000012800000090a2acbcc47d3d3b5de0fd05c6e9a434a45f8df67d84caf7cc08438cc747261ad6f7d5182e8b4ff771f889ecff987fc81ab3fce9803f1f66ee6042e70026eca90cd8ee628a14cb2819b80062b9583aadcfb87a4b07b4758b162c4836d7e3c65d1406e4023e50cc17103ad1f23a9913eeb466c9626c3753860cbc973345674a43a3a9c3752420a8d4d0968d1dc8c051e0d000000090bec08567dd021f5e9de396dea483bef26c0fce90d2a9810cc0fc5cf979273566c8bcc1a5b7348f8c134c6836db56f06e1ec9bf2f22772212bbd346121021a3e06630212745414ca8ed09a68bcecae7ae5d657a5d0aeeb0787c601fd7ac9164c8dc78b475ab03c24fcd0d069e26386e24a339e387c4aa4aaee8a982836b9fc4f7da4043a4d98acc25a6d734034ffbad800000012800000090529e399a8cd2758e15cd6288caae8a4caf0825dc10342864bb3420a5599c42935ef54ca3c0755585de1d591be4d0a077bef0d0c7bf6a960ce038af375b4a2353866fcffc2e2ed3c9c126a996472b64259ba5bb465be59a2d08d91696dbc24909500d5840aae4a2af179bc4eb0a409b4b8472380e2f327252ed83255cfd2cfdbf6ffd43caea70a475fc62ef5f0f88aa5e00000090d57807dc2c257bb03cc5721539ee708345108fcf0cd13c91142f134800d242e8435773da485f21220c059830b664ed61fb5fbfd7a4815b7e33ab387b4c5a6b2cd5ab5bb7e22608bfdd5b53f95d58276dd44019bf08893e0b527c19c3a537cdf76bf68fee369d43c622e64a8567ef2cb84edd9ca30480d88489f1e31117c08c45e575ed09a075964611b419728712620c00000128000000905c9800416b095f77831bc877a355db545151d4ff58a8d9347dc9a982366aa6da5f9043e7b3320854635dd53d55cd2f283188eb27020f98ab4a2735b23d1f91df763e3272f4fbeb459ea74d93d007ca1dd1162e57b48161b27a2f02b35a7ef5998d4bc0e60b7acb4d31f781a1544d992688d47fa439a6e7e4ce9a8224e4e1c36ef85dd306d8fbdfe2246ef96c98ebd7cf000000904e9a6442af0ec3b24f0e4e472afb25a70dfa8159e23a861e53cd3756e54a9dc02e5a0e2112c1e00c8881ea179393700d41ce4009e93b8c5abcf4244cb2705507bac7d6bf6fbcc6c7e0023b0518765b6bebe0f278ba18c4f1e62102b4affefd24d94713ce05cc156a4a9a0908fe9632892c639af5c86c471a02aa3dae340f061a23b94ca42551f6b15919096dd2d80e87"; + hex"0000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000020efbe2c7b675f26ab71689279908bbab33a6963e7e0dcb80e4c46583d094113000000002adc67712c2f7afc4e827551236adb46a693d572fbb29f3993dbedbef8d2d87d0000002027378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000002b72136df9bc7dc9cbfe6b84ec743e8e1d73dd93aecfa79f18afb86be977d3eb0000000027378c30a97c642a3b78bd34c54beac15a4dadcd7a3378e66d2384c485fda541000000000103babeb39cf7cb3fd320ec225367ac5e834aaec3e7b48bb3239d0da39fa4a200000001235f4e41a2440aa28f9ac14e7eed447ddd2e539179cee4c0941e6e408d2443e50000004026f32989eb2870f2ed00774f54a82e8266fc2a2b3392b64e8199aacac71aabea000000600b6abbab461cfb072b267bfc1ecf8dc3c943736341baf11c5c829e345c49b5090000000429c0d0effa4242b8d2e372cfcdfa8bb57160715fa3640a3404a9bba93725a107000000042fbbd267a1c9b23b3ac1609b2c2a7961a0338c56d62607d5f8d6b6a29e7fe4cb000000102d81b3e2140328e322ad47f083bfb89d61aa26a26795d34e7442006f939663b300000002000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000012100000000000000000000000000000000000000000000000000000000000001220000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000001250000000000000000000000000000000000000000000000000000000000000126000000000000000000000000000000000000000000000000000000000000012700000000000000000000000000000000000000000000000000000000000001280000000000000000000000000000000000000000000000000000000000000129000000000000000000000000000000000000000000000000000000000000012a000000000000000000000000000000000000000000000000000000000000012b000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000000000000000000000000000000000000000012d000000000000000000000000000000000000000000000000000000000000012e000000000000000000000000000000000000000000000000000000000000012f0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f000000400000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e0000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000052a0000000000000000000000000000000000000000000000000000000000000521000000000000000000000000000000000000000000000000000000000000052b0000000000000000000000000000000000000000000000000000000000000522000000000000000000000000000000000000000000000000000000000000052c0000000000000000000000000000000000000000000000000000000000000523000000000000000000000000000000000000000000000000000000000000052d0000000000000000000000000000000000000000000000000000000000000524000000000000000000000000000000000000000000000000000000000000052e0000000000000000000000000000000000000000000000000000000000000525000000000000000000000000000000000000000000000000000000000000052f00000000000000000000000000000000000000000000000000000000000005260000000000000000000000000000000000000000000000000000000000000530000000000000000000000000000000000000000000000000000000000000052700000000000000000000000000000000000000000000000000000000000005310000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f000000000000000000000000000000000000000000000000000000000000058600000000000000000000000000000000000000000000000000000000000005900000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000059100000008000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003210000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000361000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003810000000426fcb9639d15aabe6d792e23ab12fb9633046d4be6911a60d64471d7560d3f6809143b7d4943a3485115d37e7596938a16c91b6055f3837640d8c36b8303bb3c06fb5fb553496e5e0b48834087e036acf99d6d935dc2ebf43c82788cb5ed1c6a2f4bd77ac2bb5474d48c2856135d18168cd6f69f77143c60b3cc370319419dac0000000000000000000000000000000000000000000000000000000000001020212121212121212121212121212121212121212100000000000000000000000000000000000000000000000000000000000010404141414141414141414141414141414141414141000000000000000000000000000000000000000000000000000000000000106061616161616161616161616161616161616161610000000000000000000000000000000000000000000000000000000000001080818181818181818181818181818181818181818100000010151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d7839353703914c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a12806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e005f140c7c95624c8006774279a01ec1ea88617999e4fe6997b6576c4e1c7395a22048b96b586596bd740d0402e15f5577f7ceb5496b65aafc6d89d7c3b34924b0c3f2d50d16279970d682cada30bfa6b29bc0bac0ee2389f6a0444853eccaa932b2a60561da46a58569d71044a84c639e7f88429826e5622581536eb906d9cdd25a2c0a76f7da6924e10751c755227d2535f4ad258b984e78f9f452a853c52300e212d8e2069e4254d81af07744bcbb81121a38f0e2dbed69a523d3fbf85b75c287ca6f33aadbac2e4f058e05924c140d7895a6ed167caf804b710d2ae3ba62b1b51297b3ea37637af6bd56cf33425d95cc5c96e9c2ee3077322fbec86a0c7f32c15d2a888c6cc122e99478c92470a1311635142d82ad7ae67410beeef4ae31f0902ba2fb964922a4610bb18901f7b923885c1d034da5769a48203ae6f0206a92855e2c01ddb3d6553386b5580d681b8230fa4062948668f834f23e0636eaff70aaa64519aafdf4b040bd2f9836e76b9dc13cfec8065dcdf2834d786e06260d10000381000000e00000001bc00000090b1c43c1dd10fa62d46ac721c5529068d286a18a86e22787da7a222925e32a3148915e9b0ed2f8793823e9b4fc52cddfda38739e4a23cbae77fd06d498b9b8e6ea0911e8e23b97a17da9fa3777775672dd21198963bc0bbf6667d10365dd77172004b20053ff5fa88f214c7cfbb1501c5aa7c9bd36a08bcd318a70f71ef4a5837863bcc1e9a539584952bc17cb270e2d9000000907686f00e315ea807d51ecea805379caef8781be53347315fc9510f3e9db44f3e65862c9d964b0007dab956272d11e50d1338bcbaac47886108b376f34d207aa5e94ae1f2adbd06d15590d0cb7061bb79b839a791f90fdfef054e878748fad2123fbb481f7e1d9cfdab406f5e8093badb645fa2cb49203e1bf68f945ca21f5a03027dfe828e99ea4b6cf8ae2daae54bcf000000905510d0f861d5cd19326c399727f453486bdc7b85202ecd41f5fef58b5078e887ec5b91f4d39ddbe24e2233747a1e65e6737b58c0850e148f7e92cb21e947efb518882acf619244aca195b5dd44fe507cf4f7b1aa9386b7904b01769ffa8211b3ea6ab32f2c85a4d03c6266632b602c655f1dc01662d283f3fb700a734f14c580bfc7a5d2228ff1a4c7761708d9322f2e000001bc00000090c6d6caffce822bbedfa8b303833ce3bb9b0b3a79bfbfcbfd3b563f02298c592c4c7216a38ea06d6e9a1a75b8ce59730cc56c8488246b4d5261647058936277ea7ca9ec6df4bfaab432e580fb3ee6ad005ae36548d2fb1e7edc2f1228431bb1acc1e3210b4f8d5b68647dbee0bb7924fe2727a6669cfc46cb82fcdc1c77e3a0de61eb3f0a7da43811a067fcc07a5303300000009014237a82b5c531c5b22ebeb4f7af578d7998537fef0b53ee2fd725d07550da956a9ea3ffbdd09144a539b5fe593a138d39e93551ff290cc899b250940749ec14f745945d067438975a090af541be7ca68c7acbec35e623263cabd630ebd11d6caeefaa085a44d44810e70f8d6a631a8207ea4aff253f47613ca512bf04b63bad9dcc861f61d81ed0d7a88fe0747c6027000000908e8e062b17d6f040bf583520ad0d92e257880d51cd470ca10fd94d0e03815062d26f1169b1ebaaf9b5dde8a5638abb39043bd0e31d0939415ed88682e4b1d0e434f879f11542360439caf4db74632afce8378f2faf1316e528cf3a64d7e7e3a5fcf322938c266d48181fb71b956c6f66353469056cf26e8fd84c3ae60f72ffcf1f9deb3363b93f5ba1dda798c93f4ff5000001bc000000908cfb998afce6a15aa54c86f27370d5fa09e60944ffd9ba0cad5da227e2df3e678226f78094eac382cd0cfa9fa7dadf181c190002fde911048c253e2f05e8267af3129dd8be984d00e9ef152762ef161f93fc72ef1ac9bd0de640f6930a88f1fcfa9ad5bf5032505cb5c90ad5a7b22cd0b2c8d9af4f224955b28bed5900a852c9418d94f1a1e968afcae8d489895996a800000090579b53b18d0bd8e4db71db680804471f263423a4c57a5807696dae3a7d751e6dd8c1340ed4e0bb63e6e45b1fa36519d89f164f4ffaa41d391d4adeee0d02273595d6cfd41d0869677ddfe43cf8d4fc6e711d983bf9064485083edf3dfac78ceddd10474c1e07234f04ecbc50cb1bac7c30248d5bd9efae2bd72baad5d108f98de87b6ec6414d7581031cca18a7a1f88d000000903df50823b273945486cac84eae73e266121418fc5756bfc8ea6809d69410c76daee250d34b31a846e74ed1e9b87ad3dcfe92600d5bf24d620319554a8c67b0c2a76816d6bbd0ad08d83a89b121a4209e751b3e1d2ab0ad916fa673ab94ae1ce7c5367d77a7e5094a96e8c6d85575e34ebcf5ccddded1e82e329b0f5d0606f0010754789ee4e8be98adc2578c242d3eb2000001bc00000090fb7cd18168f47d0ddf433e52f0ea9f4f1d391b476226feae60b6258e7e9203983154c17120ef162fb83d858ed5d89acfb48b5b42ceeaf4cc45a3a2774814a2bc080b2a5d139aa7d33d54730e7c6b72b8c81475d99e430e914d609adba55d572d9fd9a2d0d3b55765aa106faac79fc50ade6e09d2fb18631a6e5028dd30a03fd78756608d3390e38358345734691e130400000090f354980488624491d43905213b4f502c0e0cf43c88bc840c0cbf1f103e74c47c3bd8bebf1453aee92c4ea82615fb9f4a50aad10dc69dabdb8ec7c8e518f55628f48a22867302d0aedbe01330ef940b87fcbe1522609073955a1f4e53b580d9fb5947abd430f3ada138bd38bfe014bf017d257cc3cdae7ad84837ad0aac1b8fc85d7f619247c58c4b84d96bb47d61447d000000909a34e935d1b48a64b74a4b7a130471c3197ca84342093096b7d7a5e57bf4aa9d89f286b4a27619861589c15f9ff4ac6277093e2bdb8f1418ce45c88b1b94d69c8fa5194ad9bbcf79de7ae6596d0f81fd1549667d7a97d3ad96e21fa687964b2d29ea50b7c0fff8bed8e3f5df7c0b7528130bc9443be97e2cd56bfae66b9f471572223673a7027b9df9c8275b7bf3cdcc000001bc0000009048deb9e0401c49c61deeea50914ae71c267a5056dcce4406b02478aead4616303a6d36685b3d23d995d2a3c31dfcd58a4925ae67251b98bc7a08521bbc80b850b7c268e8b796d8e4436e156fd1d9f906c508132999f3928da46a5829c3ef8636ce949c5ea263c6e0a23427bea71a8fe5cc35344d0e9d61b2891de94fd71fe97e23fdd6a1f28937cd6a4efa1e3282614900000090ef21002e4193e387593866f9568792747b9dc5353764bb3cfc9691068cd7ec63509277384c3e12130782690775bfd7598b30130d4797603c432bc9b5bea6a27f0c69b05374e731c220ff281450b2d4e126716e2bee5436d160b2f6731bf35c900639a6d7e65330b75439d3c7c6245f876e2950cc7fa970ad9ff16ebb6a0898525dbff3e00564cd209f260c2bc4f694910000009011f59e99b692f90fb8cb1a1724ea0cc8585330f95afbc3393e707c4d2a7703cd6ab9d975e56c7c1d10863b3b139958722f4af95cfda6159bca44175d9b5c6ac481caa2a86c6770abb6d6925a00213cea8455e87213b575da93ca24ee48e627145db559c962519911b1afb7a47af5ba0460a4a97ba00bd4bfe9f52f194c5e8ecbf6427cc4560227224f38c07b7e59a4ba000001bc00000090375f55f2a7cb6057626722e28363fabd45f30ea1babf12ffe3184303eb5788010d104812cc4db5d7d18114007d9f4314e64fd02acba6c5bbf83ccf2f35446b21a1e7955c55e6859fefe6f71f2e16ddf4ec1ed3c5178daf2c9e5613c3e9aae3201946fcdbeb0e8719b01a18a25905cca216f034c4f94ace0ee4bcfb8bf0b40ff0cdcd8f1238384e28df63c1ba65f91b540000009025e941529ddcb4ac5e93de566fac91cad1baa6dae9cda86477c95ce97c7fa81b6a3db36b657711ea0a1a7669a87ebb593bae554b66a058af1a21ecf1b2c5aecfa191f54733a32480d29a1ba026b6fc90b0ee4e718738ee68c2b34e774116f045f1138ed8c99ba88b98affe4806efa98b32435d368c6f34dde1fa09715624ac0611faf13adc4a8a6eef04646897ce61af00000090005b88cd11607a66bd9fdf06b8023ae16072df1d1a7d223a6d7d37049dbdd417043e11e1736c01af9098b6e30ae6e59e1882deae1521bd41b011c3f18fbb569d20e9af66508f743059537843cd1088ca526dc1f174a8018e467dc19af5cfb12d20d09664ecfafa7b1acf78c1396f0dd20f606f27813b004b81f347c313fcc77ceb5fc33758c4042e3bba26e6b4c9ac79000001bc0000009011e23f2328a3cdcb9652a6dc95eca90584fe46f92b70362a33f8374faf6c26f54650df91601eaf32e44ef840c5c3e0e5d02e51198bd57920a5c8b22aa96288c430ca5c462b8aab462c9222c36adb884bf5149978707ef563dad2ab59041332b569c79cf44e915c5aca22b75ae7218e996bc17d8409b0cfe5ea948fa2f95162b00069e3046acb65d5e245756709742b6f00000090193875fe2d757b5c35f529652a27b43f96d9866935e92806baa042413da52f6785a97ffa06741bf7f3a52031e8f67bc4ce40528f304d1e9ef6efa19575100fbe49f2147d7e0ec69f0f232c239fcce61a50addc45875278d5ac24e389521fca0c6a49eda5f3ca7f5cc7f1f9fad15332c9e6548f77cc0fbafb72413f6af7a44365b67dcef769105d6fb99e6acfcb8ec3d80000009060f4380337b2cf5647dc40b9d4d198ab5c8ce6317e4ca71b6b120612539ddc0de66f6b5a42257146ce190103849efa4dbc68903f1a32164258c81ce43caf8b53aa73519fc442f2f93cb9494fa19ba62817d116704233bce8a761a13221cfbead9bba235b6567045072eda0f4da79a5abdc640ca003cc218d3ad51fb6f5699ac03207e37e457d997288c90e043a4b49e1000001bc0000009027ec222ad55c14e1b629cba9e7b024ad8b045150f409c624f1b4b5772994e2f847ed4c6fc9ba0b887fdb370dfab6bc9a029a882d513e848d0a000e6a21d392081159e910e1824ca8336326c6fd32e62ee4fb91d3bcd1ea95316ccfd428235da330cf4e74853790219bc1d868b696ce9f289d416dd11908e410c00a7a2b0ae38ac6aee983af156f7ab445944bf3c78e8b00000090c7b511f4186d7293c83edbc937e81e2068ccb3253c10908e1dbb877098d3312ff806bf187b9521032fc91802a275c546310d3ede01710cadb7fef5c5ca3f188c900120b753a91fe69a4bd6ee2c39bed71c7ca4d6d5718ca20a3c83795545b169608e6248ef7b16e892c08bdfb2e261f85863b80f65540b1a68f1fd872fd2a9012f645a9177f656ab4bf297b5340983a500000090acba85789c0bd050a43cbd36f5b1c1e427f898fcee8c905e59e58b0218f7f652f7f947cfb947b52336439ec95ff4472481c871ca400dca0b90bd36ca63cc72e884736cae8a5ea449e383aab49c4cd6fab9c185f5b03b2bb9995436b1736674b1a1f8a9f4eb1102ad1fc406db1efa35e57d04cb2b69521ca599c548a3545805d44835b5dfde8b9039207128eba0be564d00000e00000001bc000000905bd444a8cba9ec75f404b87314bfd6020f57de5ccbe53d5fc09d4ab7d0523bad932cc48f4547c19d6fb6e5dfd506051992fcaf21fdfda48c0cbc1bac1a0db7bec9bebf7c40350a275594cdb6c0f5c94478f2d8ddb378ee5553051ba1014b4ef11c72588a4add965476d0da2f6d92e8c0745301d16b48600e1818114f9b02d82c8fe9ea5faf6a6033431267feb6788e3800000090c2cbadc76ca5c5af8dcf7288d4ef510b6114c120dfc23294df68240116291024a307f69ef01a033c11adac13172bbcbd83499538f279d4e6979184bdb3e9a71dce200df699cad843448318fc765621c6e762e8115488774101f7e3fab9e5c3119cea5b3c016fa19e2fd7686d7ab0bae4a036236ffb0cd9a1de8adeb1b624e150e917033d9a6aaaf2a93533d70007bdfc00000090328f9d32f22edfebdb68b5243666e71fe47a0739ad54826a2c9022076c8ca9f6e33deb528608d088e3e1d9a4222f1155caad9f3c4411bd779fe4e48a416cd6b30dd4dc5af43781989d1677d3c9422ee5687fef2d3f2de01d28800086c3cdddd4fab892dfc81ebdc8538f69c21137b4edb06e3b57ae6e75b684510c05225cfaf9e3ca6523fa821c3769d41a21fde5552e000001bc00000090fcbe73df4cbe4ce643bbee6919bf8ce36bb0083e91ddf443c4ee83374ceb33a79ff29e7155da16a7e1a4cc387b97981d6c38ca137a53c59b9e953d528df5e966da7e28dd7aa35b22cd75f86a920715f03dca7ef0f24bcb605e4be105a9b5d5f3fbe87f5d8bc8536011782da77732815fd863e08348fe2eb2c550f8bec4410d3850a79fd9cd2e37a1d244ba3184a99d2800000090859704b25ed494302d05adc7012684828a21a45b8aeb93df0a2d1ea91e312d66a2832a040bd2bcbbebaae7cb9dd3aa225d094a55977583142576a401ad449ed0593e7d9fd1cbc37ce036af276f565a038b63875d8090ccff076ac7495b4ff9631e86a6a0cdfa74e2f677750b1a1c280fb6ee6ff516163491aa744aad3a1c487cdab16dfdaec960b66ca812785ccf1f2a000000905726064d12c28f46856076461dd95fef270926eacd55074d64c998310a4798b699e8c864d863fa24c164036b379429ba81e7b9b52afda13d16cfcb231ff6e36812b16064122a163cd7bf935ca76ed6e275cd0e355aa41880a54c6f4fc9089e66837f477a5a0dda745ac9196f94fc8e1ec36eb8ea932eb6b4d737d6d005d0b8e3f299a2c02ac881de574900c89eea6339000001bc00000090bfee39eff3bae13492bf9ce976a56c09f166dbcd46ece93ab636914d02a7392596a2593c973fc5329d8dd03aed494ce37ba3a9a8d44108be1ff4238c414f2d8b9de63fc3adbe43edfc0bfcd0b2c85b9752eb49a51f36b995d20a3f7d80b35c30097ccc4fbe50cc4bf627e51771b5abcd43806c5d9dbce141c8c002bd56ea73d3cdfb523d1b6195ecdab57b427096227b000000900296ad69087cf87ef862839d9581a851861d600fa3e1e0dd4eea3cbebb18b868000356deed62fc7f1645f1a0b088aa169164b7f93b1ebd2e82485a677b57b68166d7896b4c65fb4808eceecca609f95546159f1d2b4ebf30862f18673f1d0248025bac6c928438e32c3b087abe01b8e827c811178866b40b4e3c14f8d1b6e89949ba757df7d18c21dd7a71fc5d4efe13000000900d33920df26da7d1d6b36377566e86e40a8015ecf91f88630ed533afafbf02a9aae149987f96ebb264eb582b1fb1aef2dbcaccc9efd52801c7d4d091b4c4a6e1378ddadfee155b09597a2ab3b1901780f0e606f6e1ffb02decffbc61f6ed141c282122b58b6ebd5807f3005d365eca15d2ce2c6ee04e53f0279af43b59017f3a5521ac893649bbf5e44cb9831879a7be000001bc00000090cc65151f7ad0a4bf1b0355424b24cc6eedba544cd71860bdd03a6855fc8242a77f21ccd3ee05d414c7cc8c8ac06108cb6102327bdb2a50dba83060c70b6cbcb31c817da3bade2a008761a0055374adbb54e0b549fe8ef201b83064196cd04bc0df1500b86ba47caf85b8bf64c01e93f27290ee8f0cfb6a5a53d9e6e2e092642df9d33d21139cceda22b76cfcdc0bbd3900000090378d2ba4c3e4b6d7f41c3e40cf644800141153017fb22ed45e0afc3f3f33f26da0a42071f04441b98a1af1bc41fc67f46ec17497f1ae5e10850c11452a284825a40d124958218ed6ab40f0babea0a827dba3a9e6efe5a9ebabbe59a1a3d64cad6ff4d706d2cad4c3f18964bc3afa143e8eff0b6469324ab2f417a4c1508b917a3e87c2f826a2ae81964f3a2c5af72d81000000906cbd3d7a20ae20388791281bbf4e67beaa96f258182d43044da9f1ebd6887b1b72da20bf46b67a23096b3f8509177b9c8dc77556cdbcc916b1ed02737ce1a8b34d3e74058494773916a1d1ab7f6658b09bf396ed9a2e70ec021178f9078dd57ca020c7a36b4e75548658ac642118f8c74062ad1169c47c2ff445433a50ff23623bae3a52b5a614fab34d12eaa314bb26000001bc000000906ea8321846cadbbbd83f29e437e2f68ed651e32a80b91c23fa9aff1116e3f7d084d238933ee634c640a369880b6eb7e1df8dd518e82546a53657a7aaf8f8e7ee8fdabb094a22378a14da612310e7dd6502aa80f268fd9bc57345c0d62c8b5b3dbd20c652445f67444074c186de015ef73221e6e865007e72082b14e9a6b03f76f5eeadfcbb705c1b03d6b983e8be0a28000000905219224f11bb74c2905c836b29a468148d41fe096400b1c4a5622c876d4fad2c161a13fbadb299cb0274f3ebb24f19f994a0fc82dff8523587cd1860b97bdf73a8a389a8b933a4711ab03bd77b28caabfdcfd2ab79cc27fe4e347d62b7acc92d385c206e4964e6252c4dffe60cc0bc6d9d0e265237941efe40116c02ed8dfa3805a63b6c998936e0a6e3d220bad3ee1300000090d07856ff8c6054302a935ee722eaf3cb1b9ec87e1c99ec4d5f05b82035d25abd2919636567dff299030db4df879a1e3da982747382e803240281fd86455d3e0ccec709c94a3226ad404ba79d6548a729a9c9ad21e1070e32154c4bb5665ea38ba1d7cf28598b3c115d50283af0a9f83da97a6571fabad795c7da119c7332ce25f686ad84f40b3ef6b08328bdb8e4733e000001bc00000090d3f985d89d0e61756a2139c09fd5502a712bf54663733ddf69cb1d3101d85c63924c625b12cd3bd36ada36864bc4443899d3c0735a5c2a29f86e8c8673952bd937cc39a6101ea05ea7e29d5ca473c8c05e3d3e37a9cb63fe19a38b2143155a2412b7feb1edd44b9686dc1179e04157e5965199f3d597190cc205e17333c995c32d5875f222e11759ccaca03613f713410000009094984b569bb94bad9099f29b90ef394dd454e2af7e3407a242f48bd08cd3fc111f49fbfc8093676a0a0f958a44b532e771e676fbcec59a5d965a98681e5460260ccd9ff9afa3638d92e4405d1f26776855ed9458d250c2854550f2e7bc3b2a7b1d2e49b3f08100ce724b420bd2f3de6a8078622e00fc4616bbf50da777426ddb2c47dc704cfcd6deb695156ca4a60e8000000090beee2b163d5d4a655689e8b0c30c8510d5c9e0fc68df0c94e9fa90d88a01d433523495024f2bf686d72323d34d8559aaacc1806e7bcb45b51cf697e3dce63ea0443807ae95619d8cbecc598e60b40ea544e93bb21b8d5862942774ebcf4ab1f56f42db3b75e3bcdc61e2c601b73286789f49ffd8b16788ce30dcd556aa35cbf32e1f628ef27cda313b1b779afa78372b000001bc00000090ca71834083c2c4a2c84e8192dec2d6f869bb192cbf4abd9a7f4fcd832d27875153abb7cc2da5eb8ccb6915b96a294cce6e99ba7632ef6757d7008b0bf0958d11e1bd49796e191a0c0c8417b4923da5c0779aaaed17ad3b4fad14065fd4203d73375e945b7792260c28893e89e3474970f80a2d5ea0f524f3efb5ffa8c2b895cbd32c5a77595c55d0b02133b97519f24600000090de3733625aa8d0451bbf8ceb0be90358fddd5863068f18505e05f9620da1bff7285b56740cf001d503bc05ff9e47c0769b45c5066c9f521cba91852d64db3748354ab1e9d12dac0284d0b63545918f00f4f1bc14c98760b9f02a666ae4721eb7b99254922bdc980ee12674075da138d68b385df945e3695c337af5ca590026d81bc5170366d9e1ee6e1a6b964782859a00000090b46b4bc458ff824b02d652b9e0e7d785589131d3359e69893c85b1606bf776de78b58044d0beb789dfaeb3104c8321ea76369c5db074097f126f8b0078dffd3ce005eb18a09cf43ac4f6f1f38dd58d1bdb1dd0dae627b5528d10adee3e1a822ca5c1aa8489c0f0e8ecdec41ad817ee12d534a241f7a6a13bb0ebe6a0e6d6349939669d5bdb654bb8f69d111a941af156000001bc0000009047099475fc1633753f715412724344352f1e4e82ce39b92b0d12b252413edde0e7c95f8b3b5ebbde7a57ba3da250bf51b2cc9c8c3f29e8b50d29ea0f31015879ca8bd47f6e092955c3c45f028b68350001831603834529b9e9625fee1b2a212e006b853c3ea4fc9124da15fd610a40aecc48728f1be8e8db55a32b26de66d1790e404c95710c76baeba3e5912a53768e00000090bcabda0ca6aea4f9d32bd42a74401684c5f46a968e48f394fb82b06ed1a36b0024f41ed47e948ddd2f96f0f9e2df0e7e61f67440a2b6e41c59059f2f563f27b21b4a7960c6f2238faf8d3bae1b31c1ed8974932b1a4ecec12e11772265bd9ded7b6751cdb0ace6d0db1055121a81f8f39b86d4e265c2bd82aa63fa52135b18c7089105d261e8a7147712b67a4f41cdcb00000090e02c17c4eacd5cab85df9268d835e0b3c1210fb114be217c839a764f1ac161bbdc2774a4fad9803a4c99315afc07b84c3796c7420f48c4a69ae5cbb3fd637a34ffb4ad7828fb81961d3bfcf6f52530d0b53f35adc9f88c96612123a23a011adb9c2b8e9acb53ee608ff642307de4ae094c859489acfe3c1d85119c4a786a1af5a3efafe2685aa4747cc63fdc84e9146500000e00000001bc00000090538a0441399b0eb4b37558ecf90d9bf9d67db754fda7d60fc3deb3c4e767bcc087f8c11ee7ee94353bb1a943dc8d148f97c3172f61121fab91a333f97129592b891c45de2c185d57c1548022605fed55bf47dbbe5470fbb7fdebc757800c35880c5ae70e372cc46272caceb0003fd6a46126a790e8d65baf954669f71bd817bc0e8c9b36e735963afcd3256ef4505b2400000090bcabc8093838df793a13c3cf44a1a70fff0569e2b1356a210fd55913b23068d1a98ea92dd3df55bd2755d97b09887edf8860b310a675eeec0ac1869aae6289c3e49560b8f4eb583bf40f6e21654a6b80e7af429fc7f038d95ecffba457ed949bbb7995158b2061f986643795c68b7967facb2cd028641daedf89779cf3fdfc90d12865fb851a5cf4eeabc7911d3d8f4a000000901f9dadab38f2ad1c8c0bf1b02c9bf4d584069a3b54a92ce07141c19e7bc975ea943719224e6f93fe69dfd882b6ecc94ec80a1da4189443cb04636e92f09f88adacea1ba91c31cfb9b7474ff1d58d63652f522adf727c82127393617c4b1530d8c961d41e378e3f5f266efc1bb0b7455d75148d6c79f98c01a5d5dafd3ba03f2639b4b45678ef60587bea187af77a11bb000001bc000000903442c44f4115857ef498fa03b90666e4b043398d13f0c2523a31c4ad92bebb6b6a3826f7f9158e1610939d54d9a3a041aa38d1192bf0dc797710a83799f48e470deeca42989c44b7622c4b99ad4922673f9ca98815da551a3d3e882643bdfb9cf0987abc462b859b433c1c7e1c9e96b4b4961302654f31326b8c6ab0b751d6f267c2adcad54b9058cc6fd5e9c8b70cd4000000906d69f738576233ec2328d6bd1ee76e6b40dab296dc3b214e02c6418e007ea20470acd84bd70c7ba5c24cb4360c8641f399a5b928077b3d11ceaaa73c11f7f37bd246208926b9c1b0e55abe71413511c4d0f04e811246a32542f7df6ac2a53254f1fcb9dd9716d85697cfe7afb069c65e9c03247da7195e9d39cc81f3dbc1a5b78e989c9e6d8956376e65a68b15be17340000009076c552a5d849a23c028a8df9458c56ef39d43d4e88b83cc86a7c5f93937b9c4db4f58496390d315c983284b9b68b5a546ec81ad4276d487ad1327ceb7b1f028887b1a7dd91fc6df07232e8371d78770a860f4890bef4d66018bc2dd5cf19c19da34b5efceb81804574f18011c9d52373d6d4996811bfa4ac2f947d90f5dcff96c9a8a4a407950c1de8c24bfff838971c000001bc0000009020c2f2ce0288f6e482241ead06ee7eb7b9f7d75f81c08b0da7b8d74fcf71c226add5b6db17fc64cdebd52bace97d20a655a05db3e73cfe16a0b88f11c049bb4a3b84ba4b50ccd39a9bde94edda541f5b44fda6a243468e74b9207fcd9d0385417891fe89f8c98a7ff77709e225c2039b6669e9879b5f595f52b194051ffa1a28f79973c4766e14da62a94b96483ac44200000090899898e94b501a777a9c12fb71a41e7697903ee2467966a5fe4ebb3024763e3d1ec29fee30c778929cb1a8a7265bd94d2dc67b1f6f6c8fea74ec2218ebcfaafb86f31426be18238178d23a165e671a30f3cb1b583f31555cb2be7e939aaa3a5b730c719fc73baccb83f69c07b26787d0107c7576dddc9dbd9d5fc7d0877f5150b27379c1d0b957180db2f528caadbc870000009057a22904be7a901c8cc3780b0277abfd02cc690199f208162df307f4168800bfbf2d997b9164cd2094a905d0d990cba8bebdfc6b4f30f740f811b44d203454d4364376c90912dc515f7752494649674edce66dc97a66dbb635ae57019ec8c46279e5373b6acf992a1a49edfdc3975f89d3185ad1b2de46ec88647230df94679cc356cb7abadd931aa6f294e34c3eed16000001bc0000009025609637221fe1306882993148602ceb784483a1151fed8e832fa18354016cdf71254410e38ac04184851ef76e6fcc5aba4e9a6c9e2638e41c7c5f8cee1b13cd3b0d17035b91ad04c63ee8a196ba537c26145c76a657e3d58ce9fed1d815a923033f7f3b4e866678d5060c1fbe338741b642162e9b7b8f9c47652521db9d99936c4d1ad38ee22f40bee9bc0f9ada6616000000901cdc562761ae6a5df3735ae6520eb47449dbd832f71ed2fc601aba142d20e1db7af47e209133de4a4874478df07071a4e0e373f4c2a61f0ef9036ac6b9f7dc2ca68a9b9d42d95caf623a8d1cb579b14e8b7d3fb8364f41bb945b63d933917fccec8de9746d5a295470a0c72ae94f47ed1a19445e70fdb586d9e6e7040bda1392fe9f3824c366512191767ddb708db86000000090f1a3c9017a4f4e312db8be2df5c2ffc914976a896d42ed510c47de5e437d6a4d7c61f893b78170690fad42b3f785fc77aec0a3cfc350fa0fb8c2d167b83f3d6fcb68e198633ab4538cd326e50c60d09f11cc14ddbf310e66c54021708c305bf57fa089950e565ae42169fbae159a661e68e641373df3cd38f455f3b8ab7d502d8187451fbe918b56a7c826e97ece64f4000001bc000000904576be0ee1fb3989c2b50c3c12eb035478869d0070c9522062b4d729485a9d6ca310cb28835f7611f4c44e8a92bbce8a7b52e229ce919a836b1942920d895205469b78da41f4862da83b05228c9728fcb1865b5b24002d2cbd16df45312f7dd1321943f1f7bb6aa432bdb84462069bcfc58650c1b4bdafd3992b5ba65657965656128fe027ce897b2051c9c697a1bcfd000000906898e921b3171d11bfa8902b63cfe050b43129c7395b6408415a18ad43c9543ed00cf3932f6023e999bc2ecb19853b3f20cf8cd5dbbe8514d9ea535dc4767cc9c9e88ff5bc664641e6fcf7d60b4cda224238e457073e702b2aa03c6d3dbb3ee797493a3afb2d079389e1548b761aa78929c4654c1dc1260d1db3cc989eab174cf2639838f844efcc4f648ed6b1c635f700000090fc9483293459d113eee0962ddc86156fd3cd45d05a0c16e07b4c0f3bfa660e3b069664717211a3cc3682945993548caa5b22df049a2f86238b3118ea877ecf9829afffab51135dfb66651aca3346892221585fe7b4c05b112fdfeee3134f6e8d3c7205e0de0a7d11fd4ba79ee612e840d6f859a4e3c746d122efd2e80755b46e4acf8c38d23f9271e101865e1ed823fb000001bc00000090c3cf4166479dca196f8f0246666941e3159f4e630223144a4e7a5a3f9f275c365e9191c212e2463a159e9de3155255f0f9c3b26c82f97c1d82601be3c51eb33752533cedfa1df5a434d37565da7aeb49f6fce23d4419aa3dc6c29478260e4254119cd7fbc9734d2ad321ab505d3b3c81eaa6e991c9a101e7250d71acadce5669281a3586b524e89064a42ebbcbf9f65400000090f32756a1d3fac1cfd947619de20dd66cb5a3bab4a4b1735d81e0b70deef25533bd6183cbe0b46ce35a4fa3ebc01b62302bb65d2a6db20233b500577296657e24768a1837c1eacd099834e69d3f878ce837dd31a7b8a8ed78e624a1810a7aba36f6ddb23c411b2ee5034a366c18bfaf3bf8e32e6d38361c9150f3c008825b8be8a3e5d177c80b2450c4feffbf29c8bce500000090c815b26c6e93de18db7c07eefdd72c77fc04eede275c87032768403812324fb8627805521f427e1fe5f881739df67d2f94fe358fddc4a971495b8ab18dfc8bcd83acc0117b65e15ece82916332c400424b9df1a8c6faf744d3d858cc63d5748ef8e927b7cb58c0129426a11afea0bb752eba612fb3f3bc43b1a2c627e7eeaf83e67f54683a0b4886990b67e07ce112b2000001bc0000009047403c11095d5b37db843ee4def2f8314b6f35746d30dbcf7095a8f5bb372ba1f385c21a8261210f8d0ac4c574f48bcff6f6f328bd938542b88fed261a7bde161f45913a788d2e418e9c8e432edfedd0147966d3bcaf0b98d0df08552403e92fffc01a0508721e1ad14df4306b4308dee4de010f603093bab5faed87c6cf60ba3171a00856f2ac3b06b699f7bfeb90f700000090af5b01631cce3055d7a9453decba284c3fdc06b0b71d98331cda285eea5206b3d2849aa349f8964e41cdeee6e5033639273d9a84fd20d695347a217822773890dced57219cc9acd1c866875d58939b7436db0eaaae1e44ed46ec19aa52c5f07139084a2ab1750e25c4ba379c4e683c08dbd954495bf6290e6edc61fdbe268142910e82c6aef3873100e3e7f40c4e856b00000090eb59cea068d5d6f4ee1d8115de3caa9ac0bc1ef647dab13a897080ee608c4ac251f4ef3fef30c12a7ac13eb02de4f2a37027b245dd9f2ea62465039a847c84c9ce2058930d1fa6d573866a018abf06350b09f9ad6e6fb0934122c64e7edc06690432f62faf63bf11f2881d3026ac9bd17d2143f7bd55cecd872e6fc98494f20c9f1d3c6436a4eff1baac096c0c5c882e000001bc000000907a519a60fe33f4b9382b5a67a538ebf6f53574dc1b2226f682bb87090ed48de9760c9b3e96ff6c87a8d2f2bcf4eabd7ae4d7f7859b10b3490561221bda35379a6196f07f7eec1be17be8abf819fa543655da0b0e224a2206197a464dfc7f0f812f194256ec8197aa776989f7f6b373a3b7a9fdbd40f85b203d63ea04bb972dbfcd15e5e7f1107e6270662260771da70e00000090bfa04272dd685cca97b73fe43df6f13aec32a46040e7a6495aed0c841aaeb495dc9428024abb90cb8309a9bc381a08428cbce5642ba0c18ba4cebf593102a05c62dffebddd38699b7d0f61d9f0114e3d7c713d65175a0aadbd4ad218a71bd8c9c9822dd7aeb4fc66994e2d31402cf81462f3ca7dc5df16a2dd15326f212096b22b30f2e8634327453802da76ac65615c00000090a479c98778c84ca2d9882cc42ef49d8441e855a2f54a43e045ded34978d82b0b1b0cd89ae968660d697b5873cb35dc91d8f463c2607d6f8848ce86a8c9eb3d3acb36d049915591056ab1b79aae00a5f568dcc3c48ae34c054ecdbfed0e96eae51e734386b262e62de543bc05d56251cad682b889103e58a536508821f847033a321142d8933ecc097dcefc5430fadb3a00000e00000001bc00000090b7968a955d2dfccf014999255b9ca6e954d7caf9a303bef2814245542a14bbfe846c946b03c54c945928d7c18b4a756d3636260d94f5d69ef6f93c523b2d80e2dfa6212f9fa3c82b083f3cc5d0a6a500837ecaff099978ac1229a823df5b11facb2eef513c1b277d704e51c28551814916d644e8818d8f971e1d80f2f735644be615e57885d8568b3efc13b4a9a1fb0a00000090a10b4b7824af15267906d782f4904863afba1c3d1ed581e99e32c6c493a7846e6f846c84286bf333ed45ae38e0510284cb164ccabd2fe49d5791a710b94c8df61b337fe569991dc692314e8eca72af12107c2d75080debacdbc1318c0a8c40b6cb600c366a733fecc7f76543c4dd73ca4b237ab3b176d98537532fe0382ffddf4e4426f5227b0a4ac756c9f6badb234e0000009085c8b49b001dd30b7018b2a4a90e8485c200113f9c3c7867cdf5a4cbfc8490f3b34a056c35be01b576f39d69a86e7f60c2f6f8f1ee445b96a4449114ea6ed1e8d03fbc50bfc658c43e8d71dde864b506380ad15dd48cf99dc7fd3c82f3f1e48bc4409ae2ace69c4350fb1235f7e445b1f41e96477784299439dd984b19d8bba4489aeb66862d543ff340073d046c80f9000001bc000000908e9cc39a7bdf0721646ba3b0233c7b8631903679258eb14f27966ecc188d74c92fb176aed4fb55e0022bc417a1976da6b1e9ef8a26a9ebacc234bd030c9f930e09fadb0e5989f964ac2db2e8c08fea5ce219ee1c757de3b3305d537c5acbdb7164e6c0c5bdea99ca6e05751c3d6c8ca4abfd29f3027cd21467dda03574adf18be31acfb55227f08291fc7ff94aafa82900000090d7ea79f2a79ac7b7199608412594e9c39d6d200c865056541c65d00218430bb6eecca91752dd462ed04bf2337c17763fc18326195672e7664138f28f44afe7bd8ee5e36e5237fe193ef4c82c8d0c608bc078dec1bf9a6e693113e6d91e261c579c66478ac5a1596ccb91ed884dcf88ff8b8ed010c46d26b560d54b96eddd6b4f435b2149aca948f24d9403096efc7d7000000090cf657fdb9a0dd2b397c3c6f86cabe903860f881fa3d787e00179173c8061a7a903389c4f0ff1bac518ddccf514eefc9814f49f54ab22f25cf0bafeb92589108ffcd6c35ead1950640949e8f7025f5e3e669dd8f298498655d4f22d4908c40f6a1c9d02ff790d397440b7d0e0e64b2eb03b52be821ee39dad03788d613b876fca3053ce1fbbf61588be94342740011cf6000001bc00000090db066dd99708e02662124ea437c46cad412577c6b6e94d535f1bc556be4d2215c19428926ffe706f811af6eb239dacdcfe6f2b00c14c70ccbf2548cd3eb3c59ee030c1a0b3f02e952e3e370f39a3407711a393569c9905fe0be6048cada151dd5fb1337d334b5dc1c2b018eaf892d3edf265f5c22e45dcf843b0647159f4dcde84bdf31133bbf8b7c692b7d24c1a063d000000907bd9616b869597f8c331a4112d51429c804370966e033be8135121beb7a4dfc21ecdc37facdc01ecc6d1db6e9435ecb4833f197b6577403d57d2c09f511e496da938ea4898bbbeefcf5c776112961888d2aab79a214ed7e45d34a7699ad5523e02ae32c96a2a49c0cc5da2581e81ccf55439f4f5d0e52f5542c86a65dc8c30aaa326972fc852c514e57877fc1632a10100000090db2a0f48ef0ee0a981673c0fc77b364c4d5b02498e71853c6806e66f215613b266da6f2c3295f6a61099d72f94ceef9a0ceff8464c2b9b106bf36bf6ccfd512f56e7d026e7f680ae933978974704a03b27a1ce316c14c50bd191380b7ae6e5eed923737aebcf01092047f81ecd47c9441536c5a4579dbb32a1fa3c2d5758014750efec3a27c52ea30b68f5d9f4850e2e000001bc0000009028a58a7daf03e720795deb55e471a44b048b694f524903dd1aadc6779f6b286caaafcfd0b95d213eff8ebdf24a227ee8a2503d9c02ee079bee47cc032af93c1f513da5c754599e5c14a53cce674e1e37980840e2cbfa446d7c8e00ac0797cc6973c723f3e7e1e4f05eeb27463c1cac066661b441c36d43d223effcbb1a7e16eb9a616c23efa8643703a519fce0e3dec900000090e12682942a9cb9d8d97e0d8542d4b3eb2f61e1c6b18d15dbe382472cb8eb601c2191b239f98b6de85ff2d96b53c5158e33dd6d195bf4837dc481d5c068136ec4d31923c522342c81ff3fac8721a1da2e83d81be1e3d657a9d6ea592a634ffd4385226199dadadd8af222616ddd36b8a58b6b014d8659e76e99a4447a1773869f57c4ff69075ccddfa46de2db7673102a0000009014acf34b96562f756f3a34419fc01eefef5123364d56c35c3226813f56b7868e300d8c299ef09be1b08b38a85f759bce2b14fffcf6892f37d18c54ba9516a85fe9ed9de7293d7e7dd0a8d79ec9b069faf44f250c4c5f748b396901228c7bafcd7c89059fc0b330b37a2b9be7c4805c50c8cdf439e0f6b8fb7e8706ed29940bf7098edde8b3f427405865d2a6dedbd040000001bc0000009076e1ab6c68777495c22513e219f4a9ef4ddff594c95ad42337177a1fb4842ee0494168c135904a0820e65e95ef99548b80dacc87fe0ad7a58c67076640d3075e9f9f1cfcecce95b7d1178fbf6ae8662ac3e09c6470fb6ee972e1af3833309657eb02185552813b4231fb0dfef0046567243efe8bf958502aa87c2b08463f71ca4194bff89dd4b29b7e039784145d5e430000009041331715e1cb585124b0dc39418125664fe058a973203758019fa2ef0ea59c7bd5699c0d072e61cfade043c16f98fe730b4c8b62c66aeca37cb5914337462d7a8865ea805c3a989f184a154b8cb49305d9b15d98703e9bc55b33d3fdf0ac4cf476ed841c65a6372744f4edd93b550e48b49c3acbeea01235449807c8bd5edc26433d94016700d07365769ecdce72bf4400000090c362e0f4934778f4585405e6402fb9de60210e5f1209f942cf428510da7f03774f0121a7259defeb3f1ff6895b5dabb8fa7dcf1c7e1c8f157c01f00f62968a872d0b1d5d072d37aa0bd644d63489d0fedb5eed785b537c852f81b1def1b4dc788eb6d8255e396854a06fbabe3cc4482301c38923ad0be4f9696227eb3f5d83e99f554f1dbd79d1b64a50341a85a73740000001bc00000090a6e13c2eb058853e0865e0d942ec7d63cbca97e6aace8d62edb8429a5a2cbd2a2ce68508e01326c60881c584e71f4eecce64d2d2ad804c62ca47c5a0d1958b6bc7983aae29d9430914e4c09247d53fc8a1ecaa20b93662f4020cf7331a55701ee9fa57b97c9044d7048dbe7d1a9a350c96e0e22b1d808e8d24f17e233e0c864fddfdc7b68a8877c73cc191eca7eb9f1000000090922b263725ba81ccd8d7422243bbb9806961a47e96da934a3a5fc4603e5d5e4e6bc7d38fd31c84aa1747287d9f729869ab9c0f32bde16677534a83148f9c489920d3b29f6cf68324c2e5d864932dfa60fc97cbcd4d9657281712b022e29f5f89992cca034a6342d635eba4c0ad2df5af89904e4276ab1da6a39432ee1c0f9a8bf0ee4eacfa26a54d1a22a3d63732744a00000090c673de086083e4f2deaa6a3f34a917e374d1a9c2577ab6a85ff9bb583bbe7fa923f3efcf68348028587ee4b7be6df0a97c6af81b93af58038760a59f2d91bdd3126fdb44c80b208862924a53122a75d0d0a70b5433714b1eb1203cbf165a934a6568c68a22d358490deecff0558ccdb30e006a98cf07610a55194893c9649b30398a3301e8ba5d721279caa106549b6f000001bc000000904a4029e9184666e191b436b44bfa3a8857c4335075eb18bf413257765e02e3e16fcc66d3c2c32e339c0ae1b4f99a66232adfaf33417d71e3fb35cfa8272ab18257d412ae7c56b7c65e1dc1c4ffd6ea5bbc3f16dd46d111c38999b5d4ad141910dd482765f3cdf7d6fda72159ecf246f4ec5e09abf857d3627a4d90553dde7ea3f5663a02b08ce0069b78cb5e4f49c0d8000000902178902bc93e04b3f3a6658a3ee3cd0aef2a16aaed6f0249aef5a026de6f516849832b547d9a4e026ced2da2ede5ab517adadd045cc698446827bd8bca4491e2521ad944320d7198e23ae65432a90c51c7d0c0c6dd2bb78ec5a47c241ab318810f37025a301961be34aa75889f13fb67f39c272222d68fdbdf47e0ff5b3673b290951c674a463c68ca3105bcfde34ea600000090688c993c3b2a470cc31c4bee959a98d1af8c12d21e520b50773fd7723d9d9d6cddb095a99236d054ce3c11c6f557cfdc79bca98c54964cd68944b28f65831eb3ec6086b226077d5512c782e030b57e3c3276da66d8eb3af1b7d2d76a2a382a1b6e7f71e1c4130bd24b864dba04727ced8abfff56ad35533c1fc7eab7ef08949d08c5208ae3e042e763322cbbb0c65776000001bc00000090d50be3ce4e58c66a007f2716226fd00515d21be6161efc71317f535c0ad3f1cb0497aa0c55e3bec7a34675504334497b6958783f1090b88ee174e107759ae3706ef200b69e4e9681a67239f9512c4e86b101df5bf73f08d3fd08acef16bc9857b722a6472a6dff98baacace6f3478fa363e9500406db02772c4fd328f7bc82b0448cea431a089f6f567410d44992c3d100000090a2f72607f0f9161f24cc9c00ba8914937e24cc3d20d3b14c751076ed5481930c339928038c366f5fa1db32c055782c94b44d7e6a73c72b8370740c9a91972eb71165516107307e18f0d58c0dd51fb0585f05e6b64a7903f7e427ba93bb69a705fc464095757f14c5b86bab8352b84a0066210a190d0ff68fb88794730e6d63cd8ef03f632f8a6d28e7deb8a9df7dc83c00000090b5abd77435f66a78e2de4cdc07f5326e6ed20a594ebb0b3dbecbebcddb6ea6fd756b4393b024cff5cc9a178ae19f5088f6130f37117b37bd1bb5847f352862590f5a21dd0b1cd4060af8dbbb7300557e12bbd00fcbf953b4111477c5add912c77cccfe78306b3f29a5186bf4f005d948b3eef381081e2a8693299484d28445e4843b48c6b52582d8a2b5d84c749ac950000033a000000ce40000012800000090229388b3a431be6b9653aff04b9359a8608767dc2de0419efb0b91f247a4269f45a4b1e2a8bc2e6dda32f707eecd34cb3366b36803954fdbb8f279d852b37b33decc47345388d527433dba1449c00d03a490e6b5f7f0e9ad28301d099ac3492489f8287165f5c231389123b86db7baae55731f7d2ca674378556f89548ab1f9f037b5c982c7b7ebaa74ae94e2dc4732b000000903aee5e522b5f56e536df8ef54bd19f1c5bb1d820658fffd764da995e584a6efaf04d744beeb6127f19410af6fb65edd2261dbce88a18cf55d47a07c5c550ebd1d6425136927d40bc78a13c45da9e88993ef764aee2a53ea05dca3ed57368eda2c5a6a13b08687360271d05d835b94b78efe6b60f2839355849495a363ded3507648476e19e3cc1d7903191e5bd8598a60000012800000090505af5b4991216986ea5634f906556df448f90ee8d7c400a6e96e2d2014d9ca502059dbc82a4eb40e6422cc17d2c59399035a13cf18494af6e79de74dc243f69a18355d4ce14c1121ec65ec9722c4667e31ade9bb4ab5c41db9ebac1f0e215dc5236dd747e0ba8685ccaa12760865546d52a9e2245d699c8e88d89cd183e04c0ef8b4b6547141d247937a6da6ae338e50000009094439dac22b0f4e1a1cf2747ff890355cdb876d1133463d0f3016734159d099540628b8a81562afd699b1bcbc2885ef8feeed751536d9bd8cf046a0bdf0398984146839da849ec6bd897350bda300f04276a1b7d9abf799776a3c64294b6e4c9c768e19f0ef758e0fa97a706e6315cdc7a2370f89e301ccfb5664a9cc5becdf9bb0fd015936b984c9b4800e62b2e1f6f0000012800000090e0698c6a57315526b74f6cb3617711e0c054b0488cd2b3d413e18674be991c4ce9d6796564eaebb8977b5ae75ba9a47259348b64e8330754cc5543c3b867da1709fdda0a509cf7a216c6c8693547cb4454d17181ad1e67c51da2446c1b922c87836775ec86ed5aba1e4e14bac831021dde12ebd15223efab74e73b47d7d42a64b255dbe1929a91438ee5d82ec2c2b60d0000009072b35250299df92ec033153bd0799eae0234158b942abf7c23df05e6c60809a5e0a344d7de0b739c3c478c00c4c020c9cade6a7f3c10031c47ad3248bc56b47ac7ec58f071aabb500370c990804be96157e63e41ef9476a965467cf588f77f2bb420477512ff881df4bc89628bf8be5b095170733398ffb463ad6347e4ec5d3345e35be599a28d5e744efb625ba90ef400000128000000903c56b6d74c83e54735baca8111b0c3e5496c7cc3737c1cbea76e13761c0c086366de8290722cada540d19e8d45235e49469bfeab2b09f84607d6bc069409fc29d6ff6986e715caf2b411343e1b4e66c0aaec16ff15335f3030803585f8769fdaff07f9bb95d5b3a1f25a3d5bdc8b8420d6ab8d8c8c3146e82e330abf83ad8bb8e8153ed842cb780af11f4a7104a628e000000090d88f4ce50221ca7385fb78a7db3e639413b03b803b6bb177f100c95007507af701f9b4982607615066f02f598ed06b6f98be07c33e8283eae9483624822dcf36400e8193dfc720d12e58d57bd1eb0fdaaa014be61644f62ad2401ff2460f91b9a852ecd0c761ed2fbf6c00e721cea4f4387bc825f1bb92d29dd01e7aa31f4aba59a2188d3c2590b52cfdd921d7e8e57c0000012800000090aab78c0b3962855a0dce967579343c0137331b1cd622dc347ba3b6e3d66ac75c602484fb7a24e771f9e1b3f9edf768b86614c5e12f5aab14023a1f35e7693c048468d51e1ad2f71bbe06deb0a6f25b348372c25388d1cd7e44fb2a01194786bd2661abf38e323be07ac7e7c869348b6a0fe03fe712c3c2ba944353e7c161715aa4f5a6988a0531bdb757a5b96bdba738000000903dcdb5cead4dbc2d6f7f4dd0093b1c14f44876543bf016543b15a05938d6fb66f17f02a0e5467532f8441e0699e20acd7060a2b6ebbeb04f65af86b416b9a081a5cee331691a934379450b2bb4ceb4b1cab8f5fc861ac3d2acbb6485e2e689c91e09052ff61f9a819bfbea097cda0c6474f8a788a00cca8d4c863738bed83bc8e16e0e9759f9ac190d2b28987ea4231200000128000000903cdefcfd45b1ba2d83be0a46ab7dd242f7e5e895e7672f50add523a0eb6358f94c04302bf690ba420aa42a69e82a4f373f1887e7698ae51d11e258901baed21b6c0d6c3d16de591910540b9abad5c89a96bdeab09b27a752e94dcb2e71ed8571c963df9afa3d8d736b0621d941429139aae3da4fafa8def367250ad17cfaddf758914fd43d6901a92473fbb27726668900000090985b0016d8588b238dfd905cd864cae91d3ef4474833887e1a0119f5b8e67441d409af7bf106a2a9a10b887bf8edaa4d240f2de9cd0d7a180819412f076d2502431195001c2cb2b624c249519933e40cf7918e9d8856a8bd927f761bab2a4d2bb1a8292f4d63107f806f7fb971d4e3dbfacf5798444c335bd737a5e00c78f148c754f8d761c3ff396af00e6808fd01f0000001280000009001ab2f2f96fcaf3ee139547dfcb20712ace6ae915f8d86d2aeb96a8efc356a2b0c9e40fec9b001f22ebc49c53056f781aa0145115822ed88c143f12a353d5f0470fbba36e85ef4491c85c310feaa6a20093eaa78eb1e49c815cca3fbcfa32a942f90dd82ff94fdb74d66a089fe8529fbbb6046b30533d144f6a537fe8fd734ce32bbcab3bc2019aef32f77692397b2e400000090ce9fbb834c6c927a11fac76cdfc44a00edcae62a5d8088092a2e7545a3b4a7d6d58566a30760b7b3f4e58fbfe571c4d5c72da8be7b7999a7c29c70a02b868ef5558b2b70b1cba583d27898bbd2d66c2923944c85d4fc77d36a5241780df91173f2bf13e9c814f68c1fe6b5d92d46f7c1b2bd1e69ed00017262f1d8b1fe795b498b920a9be2086eb2f00b8f652964039d00000128000000908860e1a69a9e2cbf88e05b93915ebc98e8b609f41781ac97fafe6c3e18e8c276e6faaa4b252c784a9b8c09e1caef1265e0f2c467d6272fdc889e539ef0aede3684937782e4825d9aecc975e09770420f04ba1333c84fee4dcc917d6ee0e567aa421f205839425a57597de409eecaa93d3a4fb262f9a99c14bfec03e1437b1108323ee30fae221f925397481c6dadcad200000090966fed22f972cdb59d1aa2e6c28c7643e2d1a2dcfd4636345c21e9d2c702dd6e9c97eea8fa2b4a5f2ce409f9706f1c4f7061ba0597dc1772c5252d3f5c0996e17fb44db31970424fa04c39e4f9dfe1df3a19ef6536cf3678704566cead85577908d2920d4b8bdc43c8154712f0e994deabb13034ec5db65ed7f1a0099cce611f0005bd6b2d2fb8168fb09b296d6d3de600000128000000901e24bf1b818df3b4f0c786e0f65346a46cbc6e32a4b3dd7354a3e49e45474fa4e90e150e00d9caf47ff6cf2ef7e5af39576ad7b7afd93a1271dd3e6cd08bafc90cfe66772ae41fcd3f6f184fc55fcb61ee464c44442f22a5d6ae4d8423fbb03d2ecc706b6043e1762d51df7c4f72e4bae329325f1b813714178ac415fac87060cea5fc7976804ea4d580b5a6a9f2bc320000009069200cddb6678b395690d22a92613469428b1d487c79be4450ceb95273f6b409c3dcfef361f1a0fa88209a31eb26b67cd96374df6bbe2b32dfcd867d00d8d5f4030e3091d200e4d95a1c0d511f8f37ba31378e1bae21b3125818a75f52fc89fadf49b10125af4c66631914efe6b68bb40d1d6701e52cf14587be5e697aba1f2427d67356e5a9c10f3c6454ed23af43f9000001280000009075bdd58d142542120664c3a621de692476f985546d6e644cc10fff950d99e39c3dc88d84dee487f947bbb0f3917c292f4276e9f87fa5724f0733daa0922f338c1cf4ad40224cf3ffa9e0ef29349903fceadf23ab67bdc42a6d97b73bb85352fb539252d59b6e7e86238327d630e0a4f6ec9d8931377580c4f6ba30f5187e4d59f60f1acd6fc96ecbbf94f13a92830790000000905ed953ddd64ef5fbfe5edb5fb182de41aa7a1fd5f9e85b6d5ea74cd8a7bdafaae31a434198d8841c3718a07ce17edceaa7e3fbfef7c0cc8aa893fc8115857754053d8c77e1b16cf1feb8e3d0860585641d188ea2c414831f3624ab59d835851f1bd033090662fcf445ba08f4876bf71590e5bc3c32ceb745088cb0f2ef46272da82ee42ff496bffc2ac37a20d2bacf4e00000128000000905a341b6ce0ede8f5f8f6e8d32e8022c11d0c68fcb7aced295e2ad1a83a9596187a46d257d062ab1c70721d5aa577dfa429081d6a355708cf37b903ca19764a3c0dab929758e5193aa8d12150c059339aec63bebd51c3092d43145cfb9d3c451dc39d6381650d37398521efabadbe3c1f182848defb521b96f8010d5ebb0d567cc1a35b1f92239a47976e2b32989ca7d30000009073ccdd690f4eafe2eead77e9e089ba7511cae490f3538e6c84934e714a4e9fe512908ad79cc5ba5e0fb79dbd9e583c32a9bf87ee48c96635a9a75be8c35194ea95499f3249d0a1bd568e081c6784f79a20467f0d81545aa21a7e38ecf6ef9d94dd9f5df93bf563720b644bda89f3c1e229db4cbe6eb0dfca5adcc805e1ab68da3cb812817251b02acb103aed40541a2b00000ce4000001280000009026bce16f9034980ede54249ed45fd623470304ac7d5c290037b743d785762c0178dde6a5b11b70f67ea2aaa4bdcad784ad51517c0aad8d0741ee9896cdfddf5efa8cb971bf13704a62ea56426c5b5b28dba5b9d7431f3b703d16ca928b14717ad847beff58bd00703d469b04ae8d5673c764459d7c70c487e2b8dc56921f3772fe890e19d46937246b6ecb53f4f50c5500000090f04587d4fab3a75048c04fa486941e4e874b19ad77ad780f6074d2384fefac7ce05c28ae2ad4bf50e0025bade67aae8770584ba9992b15db5533cf3b89fb64677adb54c862e350062e733f35edde61fc884a5293d24e20c4310964eafb3c5960d7d1def9a6f609e98b1022a692f0fb786d81df0d158fff49bd11a990c3122e2fb83684fe6b5106cfa2733c6fab9fb88b000001280000009035a7eeca40c8e2bba2ed2b757b8d6490f44e13e161354541e0075ce961822a4eb27b051457f9e88be8684213c8fcaa00ad2052fe2886e701cc24d8d6f7a4aa4b3597c7d85613edf04dfcada7d7c39da86b077d97620635b6dbd29824e0848357be256b504ff5652165c4822d4ad820ee2c79467b423f5d31adb633612b5e5b15777d6ccae55ec70d99a6c10c030cfe2a00000090aebaa1ebca372bd33e7852db4163446d886090bcd05d6e5355d11047e601463f4507859694c8e30d11c9d74acb02a5c7bd5eed8dac2d9f0e5d0281007233a02ea4c0ed665441aa57c0c7082b942d65b53e605ec7d3d89af83196c02996622bd3ebaa5d54b7d4da94727478e2987ccb4bfb81a153a99603930fd609be0e6be983e1fd59f539a91df3e9f1763142f9a6ce0000012800000090524c471a92b6d7165edf28ce0e54d66a6f61e270e338d4cf262c8cd180db7be85cec5365b6008feb5be52c99461328e1ebdf6893df6f9808a74e2cd5fc6c60919000a617cb9a4bcf84e01cd6404e7d5dac142e826e737dc8758b90c4dccaedab94f1cbfd7cf053d8fcae94bd309f201831e0edfc6ae8e30457e0c0371031220947c78fea955d4680fe39ae790b9e963e00000090d3bd94123ca96bff2dee2aa370efcd404e118f1180d79530dedaba3dc3154614e9915a8b0901b005c288f061f03c0c60566d357ef772387d8e61e8f4c2b8654c62c7cd23c3ac9c033d98892b1ea04ba5b4a9aca46eb7a128045a09269f9bd4fee8402b8be68dab85fd929afba6f29a7fe738be2ce0e94820e0f9c6518bee1b44679c9780f200b5c6261802b4a0be2b1c0000012800000090319c0d94159436bb81fcc81be0aaf9e3118a5955348108a8fca1f4a77806ec0462f1899c78170ff4176f5e97d5a8d2721e7bc03a8eb6dda0351850ba330a2b54cb7128286d9ad0d7b58c37d112b2536db4d6a86d66a3c8559d4cdc19caac191228d72367ed35f94d5d2dd7043ab074a7ae22ffb167347637c3ba2ad3a0c419d1181e67b061a1de2ac917f012999b1d0300000090065f2755859b1133d853b59bcc80bb73af1189664b3195d624861c07c9eb878cfa8687cfb82c4f851b0528473e27bb9af77059ae1c4e23a5f2372ff75857075a753a9c75875ae2129823424e66d5d78f84afd052d61b0a1b31a3dfe6cb1a806aa19a50d5b9ec157d2facb4f563abcb83cc3b6aac3dcc528c329b4919a355907b36cf2ee524d926ba87092509e3d808f70000012800000090fa2875f8be9eb0776642aa5d2bf2bc615e324ee515e2d4bdf81c09de95cb6ab3136288cc91a3e145bd70fc686df1754f7162baf30e724733a98258f91792da9bbc4264cca247c12d94e1fc2f2e45f9f931060c14c2462f6b0cfaa1a6286c03d3e30d81cf3c03f68af6d107fd42cca7438ece9749058fbc2328dd9bbe02f4f8d6d2309e6c3b0001af3cc932dc8bcc8ed200000090f50d7d32ab91cd8c3aff752e922844a0bfd5edef2f6bf8784360fd5b486e44288b07615b9d580f34a302222225ceb2f04d41339ec684e624163d4917bc8e42193fe14b0c1c88d68f88f9067ef677bcb76c752f326f2e536bf531c021e543fde42a5aa9e1c499bee05f6d78647b9d9d5ebbeafb53b8604767811f8e2ba029cb438e555810adec3f85ba2521675c2750440000012800000090d3e5f1f0b7463a85fb887c73b59cded50445fe4e890daeedf6a576380d2b3fa11c7518fb25af49490abbd6712cceb4305f3d43d61349be0c9b4c393a8a6bbf30f17dbc06c2103572b1b5cf22f86daf99eb76470d469fae219d26c1dbd533f47cbf1a3fdedbc55ae23dc2a789e51a8ab28eb2a0f292ed0c9f7253913199ccb2a910bb012a2ccf4630fefa169f661ad3650000009010599e417943025a7639267a1e18127c1cd439d05a1039a059f8ab49f9dcef63b890dabfa6096ec7fa0ddad844b29842ed3ec56bd6190c811da59fa70a07f9f5571dfa7217aa63717e7e6409ed81e37701851638904cc026911824d6b25c702fddc156b58f5f48ef411176330f9fae31236c67f30a1650158369ffef352e4ce3fc6a4d8cb5fa6ed9c5ad9ad3d119701a0000012800000090a389db0fd184113a4e4b46aa17388cbde640bfce3325cf61bd8ad7cdad47770eaa77abe2e690f52076a252505704b309cb8851cb69b24c95a87489136e6f73c0bfd49b94a4ef197d01d754d1ede85e4663e923dadbb4a4b806746f2d1ed9d992e06e2dd80f320f827db44059e1716ea92a1fb7c77e0d624d5adc773982b6c8d6687b699b3bdcd74edd00d78a69658c7c0000009028f5c952c4c3cae898a4269ac8ee0a0d4168e771a6f8d58561fed45c016c95c495c721923916f6d187b2523f7df94e7db8342aaa50f7a8c76823499ece6d3a53b1e095c4310c0225f60b20a91b86f1d2fa94c0ec508cc26b7b88b01a4b3b17e06698d586af6478253b877068f6a846f18fb0179240bb56e4e54770b3a4bc215c2be2b50673dbfe7bad40c07f63bbeff30000012800000090f2c814af6271db7c8e9d4e66cde9621bf22aef0134da41f47c35f71e471322fd66ebb39b40b974ca386bee6418469d5a1f3614758edb5ce1fa9636e03092329d8974e695dbf86e8da247feb4968a7b061c729f685b9da1d8b8635e04567bbd2b33eba0c833ee0f0bed056365873336e905c137ea0f17978259c2ea780c8c25ba328af85d9d98a0e3d938ae4107e42332000000906c52a4d740a9a815af7a7e95ba09c78226736edb78e8802d418b12022485c3b7ce79547cc3dee2c8001c0955d2601cf7aea7fcef6a09f1e36410a28a233be825bec542c9dbb53b6b2d203676d2c62b06d180b8fc42125ab058b9155a27244b61552af9f69f214acff4373f901e83c64a49116c2dc7b9b312b4fc93de2bd45a90864fbdf25a4ff286563a0ddcb50ecc770000012800000090bc8e92635d068e8f45cba5df8453691e130a9fafddea39c56f766fc987ee093e76ab21755ac24471f4bb049e3e71af69d9b12bacd520d6541f74909f903d3ad05830a6453d20c79fbcfde7ea423693eae7176bf537bbabdf309fb4e4178f40ac2eb048540dd302daf6deeb69aad9617361c56304a9037a66d39a94c487235c862b95d933caafaf0a964204eb00b2fcfe00000090f1d1f253bb1383ea73d63955d73b688b3daf0a019a1747ece001a07708988e629e5cd0d557e8f2f674db96e904a69762d1bc2eb02083b535e7767d14ad60fdebc1ef938d8422cbcbf9042ffc27a76523f81cf17bbb658d9cb951fe0bf9335520c00aed26cc37b7ac1eebd297ec02e90deb147a3aa5f2933973c37738cdaa040db84200c040225e7a4d412cee477998210000012800000090b864adaecd36d70266605faf87d3eec7205abd549042d44f60ffff66ba0fefe5f2426826b44c2779e144f28a162d06d24efe8cf29c337333c8ee42bdf40e0f5f836da60d7ae07495f821360f22afa06e85fe1ff0710fe0f12f2e1a053cccd9a89eb8d69b12d0604daf20a79e2b310d069746d271cfd788b448aa9c907b0be49faa548bfa0eda227aee965c8f4b0cfc1700000090cfc24b1d4dc86a7e0b31a4b03db181ed811ec14b156df7da4bf44ec6a19d7154ff5b7543d4a117176f0cb5a509706a19736b86ce8027dd3ba62ed0e1bf93bf2686201a6aa33bfeac26fe49c2a91dedd85a8d0323668373868b40e65e662edd3b079fda7b450524e89ff5987d8a5ede5117c005777c90a426862f76bfabd4ee3f254525478d37403866cc1b536b24519f00000128000000907340b3e06f362d0e25af7a0054eaceb640eadc92c26ce2adbd225e40075c48adeabba9a0286360795e5aaf7ea15560d1e9ccb9307b854729ef54170a63bc4584cc33744bac67b66475067b808c7b3612837efb49e00909b129456195dcce20256d5aa533a5ccf6c81644deb9226458b01fa3882da3ccb1cde0778a76e0ef1de5a397b1a74d1015da520af795974fb68300000090d2f4bebe950156f5a477fecae6f502f2e12d0a6df4029e8b4bae461d325db1d3b5812a5bb055406757f41621c76865b80d6be58a8bc5d9916c0627eee5928cbd9531e7977707dccc5c37a1a8a91f61395a77fa5fa5b7a70b8e677d311963474577abb368e9e3e571a3161f82c4603086ee0acf8c450ac2390b5e11e41c161ed9490c0605cef9916dd7ab4dfc8639aaba00000ce40000012800000090e549acf82a2d39497468fb5b925614c77bfaad29c7c2097eb12481b38da2cf526d83815bf1098dd63315c17a3cadc92ba6f513002cdf6a388f1de593f4b1075fcfb8ba1775f0b9796dcca84eac6eb940a7c45a9fa6c123681ca3d9e1879f61b842aea625a7de75502dc5af86d6b159b7cab7fdefa98b2db1345d07cf7da54dc5b1735ea31d2e75bfce4767273bce88cc000000905b57379b21c844b13cf8f3a037091cb3d0badbf3bde4c962d6b9c3c859e71295b1854091dd1c6668566ad727f9020ade69e19d34adcda46d2c93260a54929ee78d0f54cd90deeb0fc3d5860b7cf373b1012cd224e4bf27172a5741012cc83dd2ce8afbb70c32e3728fe900fe16284ae8b91f09055fc133f32923b06fa3f594635d85f964e8592be2175673eba3b07833000001280000009068833df188633bdd6038b3056a6f3c496a25391b1931b2f7cad08ee450a9a2a319f9781f9219511d9e739d99a6339a386822f561ffffd4d03d7fded85c2aa60202b8b67c104a85455a269fa8ffe4aa104e510b61731cea00eef07a943ea86867635706b6dbc509ee7e710247f254ce37315c9430368fe5e21cb5868b0f8c15d0494baa5aeb5c6b9a3b2985b3791b9a6e000000907676fe247936a75a03de9df952f34f0e82cdb40a1b057f932f6b913d30f708119d8857a1048511ea14e1710db2b90dabdc1351d9521f5f3064367a1d2db14fdb33bdd44e155cc39f61795f024ef460b307624c7a5e004dbea07c89cd271f921a21d225bc1cd87db49e398e1e8aedd923c9032f07b7e29cb1ceb462a59a6b2f9aae7cfee6064ac65578fe46c69cac75ca0000012800000090811ed27dc7ad47725b36f2c99ddb7e4cb66d6ddbaabe12556ab48b82678be51ba35a2c8401214e1c52e839a8a6903345b56c482977ab438bb64ecaeb5b6dc871e3a2876cf72333935d688ba754f397f3b96c795a3b292eee1f0def244ff2d3055cff950f727aef6d10ee069202325a8e154d526e08bd9dca4115276a99642612af60bb882d18903d261879dcb83c388a00000090e215b3deaf1f80a0450ca99498b82978e61011cfecd2b62ac1302b54f5abf8353658654997d2d66aef07ee1cb22bc33ac492274e548657b9d72c33e15883ce164fb56df05ab50d4cabc5ec4b3e328e98d595e36b1b778577cd9f07aa530d8049b4e39be4470cb7ae5afda18939496c85969fc1d3859992dbaf71a57ab515f4ce475bfbb014d1a3b005d698240f3569680000012800000090393392d7b7dc83078bd03116d5331cac8598a7d3dbfc13828d4a755de00f03b2ca88481407fbe04314601bb9001255aa51f43898cd2eaf0a1c3e11987b40fb4ce9d81f838de5c7d1ecbb79addcf3e27662abe805244c5b3d6fe5bd6e542eb329f48584e30d09d103679f52268d63abcaa6d02a0e9596b1c5f4d54c4fe11d8afb084494242cef2761e6be2b26c69cf28200000090082406fba8c9ae1b7c48b88bfcd627113f0d3f8c845665439fff90506e6d10257cdd5039c84c2b726a793c46fd7e30553bb33e2b4508d626e3d1deb2742683b26bd96abc118606f6fef1e0254203743f7ba5e122cc28a61e7c25550ee83cf7f363fd0ff7ba950447f08f868332b7369af8dea778aa4f2bdaf19a6ea422f16b9b4336b90d597c150712c3b083fb5dcb860000012800000090566d5d24fbeae58464b0615822f5dc1a5a03e6387afc0f89143738c3a7a82202b4d56c2bfc68175482f4cc3fdee16e023931a4dd760b719f0e31325a7198dacd88e32019efcadbb2a52b9701d23476fcfaff2b0f621845c3e246118f5c45e2a6790536c3984ae9a5984b938c3297920d7119a80e9e65dedb85f624888ee140d33fc9acd649e269a3ddb1f6bd31663220000000905b94546562731f1304d142db43c5462d32c17a7571b9a3fe2fd8b6c594b07a1bbbe4307e8b5c8c79213dde97f4a0ebaed453c4e798137e16b7d97c5882bf505ab2febbfd0a9bc802ea653874f11d6428cbee3a13a18eb54aa068cf2493512935163c59abc5d41900687c0222c084e85b476cbe9e2c1b367589a987f8c85c6b31257a22cec6af243663028f6d04b83955000001280000009047f97e0eac0aec21000ef407ba1cee999045431c0b8dd55027275e04502ab79ce4d0b6e5e86e8267a4e0234e029b335fe0a5a425638b5c07cdf37819afcd4b1f89246b02dbcd453bd7c2b0b25334f835468e6898a5c6066e62b8d869892dd294e05c83441841902532b4b5614c537bcfe557e329721078343e340bccf1abd74caf709ba0764352531308a7f5abbdf36000000090538969d6d762b76699c38f562873016b04d775021b4e9d2968430a2b3037c9cab3ae7814fd6dc7540e1a776bc4b75ebcab68a5fbc3eef095bbd0897517073639167327c8ce14e0e383c48475123e958c0c547ec4a2304df37901c05672f909770f02994c6f952eb5f21be1782e8f9b58df9967dbbf40306aa622ae9b4f8ef918105bfa6490e6311df30d9caed97e65180000012800000090ecf13baa64eae5d4ae2a2171abf58b0316e596ffb4e45d8b04fc6b3ef7baa3b6f144c69d47e57f4ce71725c1d23d904eab72f1e9558b38a2ec2e03490753746866158f8b5a1c1676b6c104795c2c72b46c02a0a24b09b0a167cf793e6d3a8fc4966487e6b9badec4505406c897064d16397c8f9c19e3be5bf2b6d836598a05aac481b44c2279db99967c7af13f9b754300000090ec3107180cd218bea3427fb271d8ee8dad175dcff2c80f1d9546beb9e3888f3da8632ce4672f5a4d826d219dee2704ac726a25cf699bc587432d1e76c5370cc090646ae5eccbdd2fd291b96c28f012c846308c58341f691de11365856500c6ac11ff5a20b28b39590fa03cb51730afcbb5651820568c5287dc99816279536f9016d8d770bfea03cc7dab39d4727b62910000012800000090b10f42fe05e1e1db07bac42949c8ba753f1c1185e27e061e5c73b8cf47eed8f356f38a8253891f322bca6bdd7d83933291953bfe0f225edeca42a4903bbdc73484d2b13347bb5b6ed7f48bb2215cb7b7cf968b29972e7ee078af4c634100613d21fd151fbe189aa19b395ee32bd671fd7bc3d1f9b8add290760f8cae20afbf91262267fb012b19527b11f94a35b5d8e20000009027517a0b48a659823f506f85de46054b9e586a0b9ef8de12a8d5f126d849333211c3e95f572cd162217fb7512eac7a87e0cb71a81c004a8d33d97e85832c1b7d33d615aa220cddc2ae5389bb239b8e29652b02e322d20c5e193bb3cad2efca03afb2d04c44815e56e2ac9caa2b68d32dd291ce3da9c7debb6646c0de58f4e321df762a3cc55cfc30425221a2f2cb26d10000012800000090471e0b99b2d33a1c6c46137b51404859c121b605ec6ba2d051cb05000ea2af1038fd2f55e333b12143126d8430e576a3102a4cd9b7d8b28bda0fd68c80f9f4907d273e8db07d35367eff9fc93d715a1e1e20c80b76593addecdab503c944603e1ace2d612f482eec9d23e9655552ad9b5d6805379b4ccc26195ad730f4a192cb4f85589fb9f4f0556480a78593348a5000000090b56978aa1d94ff2e2593864144defcc9491429a862274141a65225b58597d15b8eb13ef060cd75c33246df88241964c87eb08f2639f24048a0dd94bbca3b0fb4a6fb82916f2b6fbe1d05504f02fd6b4a67b58c58085864dbb6a26f7359a1e256e7fda12e9f4714dbab35fa1ff05081380bd351516c68b542f7068f2ecad24139b59055599b1cecae902973dd934007a400000128000000901ff8c6da2a57ce3580ed242c3571d9ec27f35ef436367a80bb500e65a97d49e48840fd757984eeee7ed4d7b675e2fa761378d270ceb582a96f4abbb7b90062747ab25a1e6fe505e7c3f2187f38c8996d773676380161053f7eb3f8c97387aa9c6aaaa30b97a546478668ac11ac2447f8845f1139820d679387fda7ae38627b95d2e1bc4a1ec6c50c02b6001ad67dd621000000901a0f38d26a55c352cacce5719aef19a35195aaa7a0660322ecef21d17cdcd4beb2e9fdf909522e68ead4974dbbc61ec727b7c192d649ba9402ef11bab69a4820c4ac8466678684a4561b79c607787a99d0ad4c9bcea79c0f6141417e2b2c08927e61c1412c6116fd383ce2b39f5284887477b1af8a5d4287b6e93cd9f6cc2b20bee631b52586254fca5b4eceadf0fc3b0000012800000090d454eb3e205fb713c0e1f57ac87f7486e283bf5373ab8dd7d6814b51e647c906668e53fdecf34d62c2577741f0326aa2770dbe8ddfa38feb113f296e8154da0bfb8744ffdc53fd9952fc2886e3c9ff4d9bec7fc40bf3b419f7cd8f9fdd94e5a80d858c55d3d8d6a78c4f38aafa2e57d344f3fa33066c02fcef95584f53fd18d77746707d7baf40f3d132e0762ff530da00000090447038e5cb74279855f930e8f7b7766db0fb5d9659e29f100a5edee666e93509b6304c0e244f64f622b85fe2edd61b7fea4129ee9877221583820e8624e32d92ab91f7f912589879ebe09ec84d6188f1afd24505c8d53576e2f4ceb6ec542df20ff8b21341611074c545fbb75dd8e4b9ace680bf1c22d34acd865dbe1ce75aa488b8c373d2601600ffdedd741f163ed500000ce4000001280000009045436ce2e6da0bbfec32c2a11b7de39d7dd557b2c44a87a827375716d3a0bfbf23e4e3027a036a17fe39610b53226ca60bc1a401c108e7a175a79639423d25e87c5bee44843faf52e06dfe9c5889e44696d84e60b00c808fc658cc1ba3a0380981f00a9b7e08844bbd7a30005ba8721b1933fb86fd11c4d51834fffcc1ab349f99bcda485368666b15f1e3668cc181f200000090bd229cf6d6f7d21631b59a23f3dcab0a9e2fd1bc8559a231695b6a5c26615528cf204d00046fb6fb2e67656576cbb6d72d194763c42e03a99b90950193d0d71c906007f18e3e1e3a7dc32ff538e16de9f4fb1ff8469289acb071318c67b9e99de9220af87f0936e67dc7dd73f06476d2c387fffde703d4375bc237c735801c71af114a06051621aead6dd13729d131990000012800000090b1095ccf7c01d057426cbf1f0b64a04461f96293107906aa032cecce60a80895415eaac9d968cf92b9bc23be2e4c28ad899b286877d856e516c45346416a54c7b851d3ee2310822c48ae58b6aa98d77214590692e544beffb4cd7db0b178bd4c73352d26295117d7fb3035e24c5a46375b0351068f8d13314c72c4791ae3ee6327bf1a69223068ebfbf3ee1162b05b3d00000090f9c41e0deba7b51b30183beee0fa118da199df57e7b64f7edabe514b9b054f84a8ac4a874b180c1173b428aede80f9065673f90cb90b50a75d45190411bca2802ec52d39a94964dc713f22e7a84e2b3951dc0147667b61153d4a7f05acc134188a20f930dbecaa1a6bf25d32efa1522651b47f92bd69e4c23a636a55111254efd05580201b50b964a1f32dc9025107a200000128000000903e89093271e6a69b711d07022e4e5e9c150a25620bd439d17551add63a0d8d884e45a3c44128ead9c2d3d8d2c3d4f92a39c8f93cb7578f1163966a8fbfc0e9c525c5d7f722a08d5c7352cb9a75b416603403f181652a7b06f8ca9600e23b4a965cdbd77ea9839d8e7e4d497c383e5e37682638f763ab3cead19ed526720b89d95366d5ff51b3f9c5ae658633f3e2c993000000906a4e1ed85756cd8c10680d71a1836879fac29cebf1d66a65cafca1ddb5b9ccf0c7eff289bdcce8b3dd23a1f904bb453359354d48579cc3fb3fbbf992a3f736154b6c8e1cb2e51c5e2c172cff4d5008dad02365cea9da1d63e00c042a9ba57e413c4a06d155fe9b9f86e262e97463597a82d7ef555baa2341426e0b7158829d50f9e4ecbe06444b6eeec16f96faf4550500000128000000903dc7fdc3162430d001f7977c0763f2ff678dcdf07c2f6a2485deeaf993ec94a2198d9ef36d7cd7f35081345641df105b6bd11a3491769f78c0d903ff1431a9e5dbe4018ab44cf0b9821ecb5ed32eb34ca80dffa898289893d917d9b1fefe265d6a7f8399c9daa180b1febcbc1c558d4746e5704d3405512a4fa1379925e58f33fa44731f43927a950a6c8babbf79a14700000090c596b4a820d0e7a8836b13d5e7f50bc9755865b70d1346e590ca091025a25e0add8c50137f8192b347f75bccce887e3f831d57d21252cab4b69dad4f17e1e0d280d147c23ab8bc57080c4812514ef05f63506743e267f89012fceb91c0688d6f5c434d4c5ff2ae87387e1a186b536e5bf6e98ed3291e5342ce9fa44c90cc5ba90387a4717d6dcb6e059145c1ce8764fa0000012800000090283d76ab2e5af53de1d18916e20f8b86bc6a20176941530f8619cc9cbc9fecc6a93b724f7fcee354a4fd6f195841ab11a75c89d33aeece8eec50e5fd4e546caabd8c04c6b1961a9fc37bc167fc31f3ebc1eb5d6dd77a1d04c733862b604dbfae14f6f9e69fb510ebf18bb64db753b0555b44b49f6fb37e41833c2cc1943c57394e0bd0dc3ce650c720e383a2de6f08fc000000900be60fd6de9a29388caf4f72b62772b80a503fd1449f8de61c72274e30ec13804f576b64345d7aedeff7af7d4b7ca95f03409036f473f35d5007585a2ae57ab94e234c195c07e0b5396f64211659a785e9a3ae4a697f5dca6a0a3ee2800208a107b12f40e5ec28cef8cd611add402aa2b64bf8938bcd1c2e4faebb54fa98cfae7b56b4a7797d5773a3b13808ebf2d3640000012800000090e1a2d111767837b0a5bd9ab4aa040837f01dc81fd1f3b4c94c577bc4334057561d356f081587fe288df0698fd3ca2d1be1fd4533a3d985a514d3b47a8f0e25c6e71b77a0f2a845a9292d016e16dea8c48597750ed24ec27955b749b8c647b1dad52191d4619762d90b395cd161201b5801587c46c623c9e9c85461a0998f13f32f3dabac687c6b37f0452793ec12029300000090339f31d769c14a357e7c98d6f5844e7c8a97ff122b25e0138d144d983c8219f971a67c20aebe049899c7ec09a3c7673ba857a507af1f5df87b53ce538385894b27c2e51f69ca75cb36656b09eaeccbffbea93c8af1dae1a5e8ab4c7c576c1f8521f27aae4d35c5bfb2868134d8dec61c60ebdb108b6f710c7e5ad7cf07026b0ab30df451dafb7d129075b4e2e29e87da0000012800000090446064bffca204655f9bcd32448d5281fc2ff2719cd87b3af607980fca826d2475cbb26176a5581312a871e1f3437f8b56e3e3424c050b0431daa7232ff0628d3c079dd7968229f7f4052650e2535473e346836f424308e4617e69233e04cdaeeb36f1952d6b42c278ae462f8c494d767a736ea0ca5eb4c7213413aa20a50b425b53e56c525fb4e4c9a4446ac4eddbb4000000908c5392f39dde50a945b2a726a59b76e677db424b16a2c17a7ae63c5b8a54db1a3ed78c150a97792402f186fbb8a6e093293ee00ff8b56cba7e772d89c79b87d48dcd17d436d11128b1f84ba989025fe5e25f731ab1fd99185783c13e2b180a344419a78577c541e4fda696c8463bff0b69cc80a23b3278f1149614cac7a019f43e484791680e168ea97e21cd5ec8bcc20000012800000090345b551d49681f065d63636d7c6e71cc73992b3b301f9591c577572b36b22e0fef0ac09ba7d7cfdd0f49a13739ef4314eb78de142191a75976123da2a49640249ccbc2af096e3900b4ca9bd4f09f52600c95f374ba110d6af27c2e1e4bc776af4577cf8cb55af20f521be2a0b9840a9e24c76aecbc0452ba6e07ff5762fd337ad72f9bebd508596940230d3a3648f54900000090ed47ff927b3a0d86080173815ca57523eb6dd5e261e587c375bd495e8bb376c15c14fe2f7086941bcee511a2845b43dce73c03193cb7f4069a18a228f310c87c2ea4c7023a863ebf36b3dd8f205b4cdf8bccfb99add2b6bb9b47057840ae0aad4147bc702c940e977f5be5fc96dbed0f578bd3c8fb05e73a83eca17aab10ff63f7781d4f42164a7ade252a819c7d8c0e0000012800000090a2acbcc47d3d3b5de0fd05c6e9a434a45f8df67d84caf7cc08438cc747261ad6f7d5182e8b4ff771f889ecff987fc81ab3fce9803f1f66ee6042e70026eca90cd8ee628a14cb2819b80062b9583aadcfb87a4b07b4758b162c4836d7e3c65d1406e4023e50cc17103ad1f23a9913eeb466c9626c3753860cbc973345674a43a3a9c3752420a8d4d0968d1dc8c051e0d000000090bec08567dd021f5e9de396dea483bef26c0fce90d2a9810cc0fc5cf979273566c8bcc1a5b7348f8c134c6836db56f06e1ec9bf2f22772212bbd346121021a3e06630212745414ca8ed09a68bcecae7ae5d657a5d0aeeb0787c601fd7ac9164c8dc78b475ab03c24fcd0d069e26386e24a339e387c4aa4aaee8a982836b9fc4f7da4043a4d98acc25a6d734034ffbad800000012800000090529e399a8cd2758e15cd6288caae8a4caf0825dc10342864bb3420a5599c42935ef54ca3c0755585de1d591be4d0a077bef0d0c7bf6a960ce038af375b4a2353866fcffc2e2ed3c9c126a996472b64259ba5bb465be59a2d08d91696dbc24909500d5840aae4a2af179bc4eb0a409b4b8472380e2f327252ed83255cfd2cfdbf6ffd43caea70a475fc62ef5f0f88aa5e00000090d57807dc2c257bb03cc5721539ee708345108fcf0cd13c91142f134800d242e8435773da485f21220c059830b664ed61fb5fbfd7a4815b7e33ab387b4c5a6b2cd5ab5bb7e22608bfdd5b53f95d58276dd44019bf08893e0b527c19c3a537cdf76bf68fee369d43c622e64a8567ef2cb84edd9ca30480d88489f1e31117c08c45e575ed09a075964611b419728712620c00000128000000905c9800416b095f77831bc877a355db545151d4ff58a8d9347dc9a982366aa6da5f9043e7b3320854635dd53d55cd2f283188eb27020f98ab4a2735b23d1f91df763e3272f4fbeb459ea74d93d007ca1dd1162e57b48161b27a2f02b35a7ef5998d4bc0e60b7acb4d31f781a1544d992688d47fa439a6e7e4ce9a8224e4e1c36ef85dd306d8fbdfe2246ef96c98ebd7cf000000904e9a6442af0ec3b24f0e4e472afb25a70dfa8159e23a861e53cd3756e54a9dc02e5a0e2112c1e00c8881ea179393700d41ce4009e93b8c5abcf4244cb2705507bac7d6bf6fbcc6c7e0023b0518765b6bebe0f278ba18c4f1e62102b4affefd24d94713ce05cc156a4a9a0908fe9632892c639af5c86c471a02aa3dae340f061a23b94ca42551f6b15919096dd2d80e87"; function setUp() public virtual { helper = new DecoderHelper(); diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 6deda6ae2aca..c4882ed7db67 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -87,9 +87,7 @@ contract RollupTest is DecoderTest { rollup.process(bytes(""), block_); } - // Skipping this because the block is invalid after I changed the format. - // TODO update the block - function __testMixBlock() public { + function testMixBlock() public { (,, bytes32 endStateHash,, bytes32[] memory l2ToL1Msgs, bytes32[] memory l1ToL2Msgs) = helper.decode(block_mixed_1); From f191c2e5271cc6714027c62209bd2e66b7bcb788 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 13 Dec 2023 13:58:40 +0000 Subject: [PATCH 40/40] test: refactor noir tests --- .../rollup-lib/src/base/base_rollup_inputs.nr | 151 +++++++----------- 1 file changed, 57 insertions(+), 94 deletions(-) diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index 22031b515fdd..0c7d9dfe3bc8 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -38,7 +38,6 @@ use dep::types::abis::previous_kernel_data::PreviousKernelData; use dep::types::abis::membership_witness::{NullifierMembershipWitness, PublicDataMembershipWitness, MembershipWitness}; use dep::types::abis::membership_witness::ArchiveRootMembershipWitness; - struct BaseRollupInputs { kernel_data: [PreviousKernelData; KERNELS_PER_BASE_ROLLUP], start_note_hash_tree_snapshot: AppendOnlyTreeSnapshot, @@ -518,7 +517,7 @@ fn validate_public_data_reads( tree_root: Field, public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], public_data_reads_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX], - public_data_reads_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX], + public_data_reads_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX] ) { for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { let read = public_data_reads[i]; @@ -530,9 +529,8 @@ fn validate_public_data_reads( let is_less_than_slot = full_field_less_than(low_preimage.slot, read.leaf_slot); let is_next_greater_than = full_field_less_than(read.leaf_slot, low_preimage.next_slot); - let is_in_range = is_less_than_slot & ( - is_next_greater_than | - ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0))); + let is_in_range = is_less_than_slot + & (is_next_greater_than | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0))); if (!read.is_empty()) { assert(!is_low_empty, "public data read is not empty but low preimage is empty"); @@ -690,12 +688,11 @@ mod tests { } global MAX_NEW_NULLIFIERS_PER_TEST = 4; + global MAX_PUBLIC_DATA_WRITES_PER_TEST = 2; + global MAX_PUBLIC_DATA_READS_PER_TEST = 2; fn sort_high_to_low(values: [T; N], is_less_than: fn(T, T) -> bool) -> [SortedTuple; N] { - let mut sorted_tuples = [SortedTuple { - value: values[0], - original_index: 0, - }; N]; + let mut sorted_tuples = [SortedTuple { value: values[0], original_index: 0 }; N]; for i in 0..N { sorted_tuples[i] = SortedTuple { @@ -703,17 +700,16 @@ mod tests { original_index: i as u32, }; } - + sorted_tuples.sort_via(|a: SortedTuple, b: SortedTuple| is_less_than(b.value, a.value)) } - fn update_public_data_tree_single_kernel( - public_data_tree: &mut NonEmptyMerkleTree, - kernel_data: &mut PreviousKernelData, + public_data_tree: &mut NonEmptyMerkleTree, + kernel_data: &mut PreviousKernelData, snapshot: AppendOnlyTreeSnapshot, public_data_writes: BoundedVec<(u64, PublicDataTreeLeaf), 2>, - mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES], + mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES] ) -> ( [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], [PublicDataTreeLeaf; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], @@ -733,8 +729,8 @@ mod tests { let mut public_data_reads_preimages: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_READS_PER_TX] = dep::std::unsafe::zeroed(); let mut public_data_reads_witnesses: [PublicDataMembershipWitness; MAX_PUBLIC_DATA_READS_PER_TX] = dep::std::unsafe::zeroed(); let mut new_subtree: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = dep::std::unsafe::zeroed(); - - for i in 0..2 { + + for i in 0..MAX_PUBLIC_DATA_WRITES_PER_TEST { if i < (public_data_writes.len() as u64) { let (_, leaf): (u64, PublicDataTreeLeaf) = public_data_writes.get_unchecked(i as Field); @@ -746,11 +742,12 @@ mod tests { } } let mut sorted_write_tuples = sort_high_to_low( - public_data_writes.storage, |(_, leaf_a): (u64, PublicDataTreeLeaf),(_,leaf_b):(u64, PublicDataTreeLeaf)| full_field_less_than(leaf_a.slot, leaf_b.slot) + public_data_writes.storage, + |(_, leaf_a): (u64, PublicDataTreeLeaf),(_,leaf_b):(u64, PublicDataTreeLeaf)| full_field_less_than(leaf_a.slot, leaf_b.slot) ); for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - if (i as u32) < 2 { + if (i as u64) < MAX_PUBLIC_DATA_WRITES_PER_TEST { let (low_leaf_index, leaf): (u64, PublicDataTreeLeaf) = sorted_write_tuples[i].value; sorted_public_data_writes[i] = leaf; @@ -760,7 +757,7 @@ mod tests { let low_leaf = pre_existing_public_data[low_leaf_index]; if low_leaf.slot == leaf.slot { pre_existing_public_data[low_leaf_index].value = leaf.value; - }else { + } else { new_subtree[sorted_write_tuples[i].original_index] = PublicDataTreeLeafPreimage { slot: leaf.slot, value: leaf.value, @@ -780,7 +777,10 @@ mod tests { sibling_path: public_data_tree.get_sibling_path(low_leaf_index as Field) }; - public_data_tree.update_leaf(low_leaf_index, pre_existing_public_data[low_leaf_index].hash()); + public_data_tree.update_leaf( + low_leaf_index, + pre_existing_public_data[low_leaf_index].hash() + ); } } else { sorted_public_data_writes[i] = PublicDataTreeLeaf::default(); @@ -789,17 +789,9 @@ mod tests { } subtree_path = BaseRollupInputsBuilder::extract_subtree_sibling_path(public_data_tree.get_sibling_path(snapshot.next_available_leaf_index as Field), [0; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]); - + ( - subtree_path, - sorted_public_data_writes, - sorted_public_data_writes_indexes, - low_public_data_writes_preimages, - low_public_data_writes_witnesses, - public_data_reads_preimages, - public_data_reads_witnesses, - pre_existing_public_data, - new_subtree, + subtree_path, sorted_public_data_writes, sorted_public_data_writes_indexes, low_public_data_writes_preimages, low_public_data_writes_witnesses, public_data_reads_preimages, public_data_reads_witnesses, pre_existing_public_data, new_subtree ) } @@ -810,8 +802,8 @@ mod tests { pre_existing_contracts: [Field; NUM_CONTRACT_LEAVES], pre_existing_public_data: [PublicDataTreeLeafPreimage; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], pre_existing_blocks: [Field; KERNELS_PER_BASE_ROLLUP], - public_data_reads: [BoundedVec; KERNELS_PER_BASE_ROLLUP], - public_data_writes: [BoundedVec<(u64, PublicDataTreeLeaf), 2>; KERNELS_PER_BASE_ROLLUP], + public_data_reads: [BoundedVec; KERNELS_PER_BASE_ROLLUP], + public_data_writes: [BoundedVec<(u64, PublicDataTreeLeaf), MAX_PUBLIC_DATA_WRITES_PER_TEST>; KERNELS_PER_BASE_ROLLUP], new_nullifiers: BoundedVec, constants: ConstantRollupData, } @@ -1297,10 +1289,7 @@ mod tests { next_index : 0, }; - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 0, - value: 1, - }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 0, value: 1 }); let mut tree_nullifiers = [NullifierLeafPreimage::default(); MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 2]; tree_nullifiers[0] = NullifierLeafPreimage { @@ -1316,18 +1305,22 @@ mod tests { }; let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], [0; NULLIFIER_SUBTREE_HEIGHT + 1] ); let output = builder.execute(); - assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { + assert( + output.end_nullifier_tree_snapshot.eq( + AppendOnlyTreeSnapshot { root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - })); + next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32 + } + ) + ); } #[test] @@ -1345,15 +1338,9 @@ mod tests { next_index : 0, }; - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); for i in 1..builder.new_nullifiers.max_len() { - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: (8 + i) as Field, - }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: (8 + i) as Field }); } let output = builder.execute(); @@ -1383,16 +1370,20 @@ mod tests { }; let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], [0; NULLIFIER_SUBTREE_HEIGHT + 1] ); - assert(output.end_nullifier_tree_snapshot.eq(AppendOnlyTreeSnapshot { + assert( + output.end_nullifier_tree_snapshot.eq( + AppendOnlyTreeSnapshot { root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32, - })); + next_available_leaf_index: 2 * MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP as u32 + } + ) + ); } #[test(should_fail_with = "Invalid low leaf")] @@ -1410,14 +1401,8 @@ mod tests { next_index : 0, }; - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); builder.fails(); } @@ -1437,14 +1422,8 @@ mod tests { next_index : 0, }; - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); - builder.new_nullifiers.push(NullifierInsertion { - existing_index: 1, - value: 8, - }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.new_nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); builder.fails(); } @@ -1529,20 +1508,12 @@ mod tests { }; let mut first_kernel_writes = builder.public_data_writes[0]; - first_kernel_writes.push((0, PublicDataTreeLeaf { - slot: 27, - value: 29 - })); + first_kernel_writes.push((0, PublicDataTreeLeaf { slot: 27, value: 29 })); builder.public_data_writes[0] = first_kernel_writes; let outputs = builder.execute(); - let updated_leaf = PublicDataTreeLeafPreimage { - slot: 27, - value: 29, - next_slot: 0, - next_index: 0, - }; + let updated_leaf = PublicDataTreeLeafPreimage { slot: 27, value: 29, next_slot: 0, next_index: 0 }; let mut expected_public_data_tree = NonEmptyMerkleTree::new( [updated_leaf.hash(), 0], @@ -1582,15 +1553,12 @@ mod tests { next_slot: 0, next_index: 0, }; - + let mut first_kernel_reads = builder.public_data_reads[0]; let mut first_kernel_writes = builder.public_data_writes[0]; first_kernel_reads.push(0); - first_kernel_writes.push((0, PublicDataTreeLeaf { - slot: 25, - value: 60 - })); + first_kernel_writes.push((0, PublicDataTreeLeaf { slot: 25, value: 60 })); builder.public_data_reads[0] = first_kernel_reads; builder.public_data_writes[0] = first_kernel_writes; @@ -1599,16 +1567,11 @@ mod tests { let mut second_kernel_writes = builder.public_data_writes[1]; second_kernel_reads.push(4); - second_kernel_writes.push((0, PublicDataTreeLeaf { - slot: 20, - value: 90 - })); - + second_kernel_writes.push((0, PublicDataTreeLeaf { slot: 20, value: 90 })); builder.public_data_reads[1] = second_kernel_reads; builder.public_data_writes[1] = second_kernel_writes; - let outputs = builder.execute(); let mut public_data_leaves = [0; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2]; @@ -1652,4 +1615,4 @@ mod tests { assert_eq(outputs.end_public_data_tree_snapshot.root, expected_public_data_tree.get_root()); } -} \ No newline at end of file +}