Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: refactor StandardIndexedTree for abstract leaves and preimages and optimized it #3530

Merged
merged 22 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
880b702
wip abstracting leaf and preimage in indexed
sirasistant Dec 1, 2023
ed471d4
feat: experiment with generic indexed trees
sirasistant Dec 1, 2023
38fcf05
chore: add test dep to devDependencies
sirasistant Dec 1, 2023
bc4915b
chore: remove old LeafData struct
sirasistant Dec 1, 2023
47a3aee
fix: getLatestLeafPreimage can return undefined
sirasistant Dec 1, 2023
064d7a8
feat: remove local leaf cache in indexed tree
sirasistant Dec 1, 2023
d458756
feat: optimize findLeafIndex for indexed tree
sirasistant Dec 1, 2023
ae6b6a0
docs: added jsdoc
sirasistant Dec 4, 2023
47bb7f0
refactor: low leaf getter returns option
sirasistant Dec 4, 2023
6d7fc7d
Merge branch 'master' into arv/public_data_tree_in
sirasistant Dec 4, 2023
7250939
fix: make sure low leaf in range
sirasistant Dec 4, 2023
240e8b5
fix: improve performance of getLeafIndex
sirasistant Dec 4, 2023
bcc8ae0
chore: cleanups
sirasistant Dec 4, 2023
1b7c9f8
style: fix formatting
sirasistant Dec 4, 2023
3848116
Merge branch 'master' into arv/public_data_tree_indexed
sirasistant Dec 4, 2023
08791c5
chore: reset file to master
sirasistant Dec 4, 2023
989ad09
Merge branch 'master' into arv/public_data_tree_indexed
sirasistant Dec 4, 2023
edb4548
fix: addressed some PR comments
sirasistant Dec 5, 2023
e731f97
fix: avoid findLeafIndex in sparse trees
sirasistant Dec 5, 2023
037a479
refactor: avoid createTree helper
sirasistant Dec 5, 2023
a217fe2
fix: formatting
sirasistant Dec 5, 2023
2a77b8b
Merge branch 'master' into arv/public_data_tree_indexed
sirasistant Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
L1_TO_L2_MSG_TREE_HEIGHT,
NOTE_HASH_TREE_HEIGHT,
NULLIFIER_TREE_HEIGHT,
NullifierLeafPreimage,
PUBLIC_DATA_TREE_HEIGHT,
} from '@aztec/circuits.js';
import { computeGlobalsHash, computePublicDataTreeIndex } from '@aztec/circuits.js/abis';
Expand Down Expand Up @@ -429,19 +430,19 @@ export class AztecNodeService implements AztecNode {
return undefined;
}

const leafDataPromise = db.getLeafData(MerkleTreeId.NULLIFIER_TREE, Number(index));
const leafPreimagePromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
const siblingPathPromise = db.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
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, siblingPath);
return new NullifierMembershipWitness(BigInt(index), leafPreimage as NullifierLeafPreimage, siblingPath);
}

/**
Expand All @@ -463,22 +464,21 @@ export class AztecNodeService implements AztecNode {
nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
const committedDb = await this.#getWorldState(blockNumber);
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.getLeafData(MerkleTreeId.NULLIFIER_TREE, index);
if (!leafData) {
return undefined;
}
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;

const siblingPath = await committedDb.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
MerkleTreeId.NULLIFIER_TREE,
BigInt(index),
);
return new NullifierMembershipWitness(BigInt(index), leafData, siblingPath);
return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
}

/**
Expand Down
93 changes: 85 additions & 8 deletions yarn-project/circuits.js/src/structs/rollup/base_rollup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +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, IndexedTreeLeafPreimage } from '@aztec/foundation/trees';

import {
BLOCKS_TREE_HEIGHT,
Expand All @@ -25,28 +27,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 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 empty() {
return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0);
static fromBuffer(buf: Buffer): NullifierLeaf {
return new NullifierLeaf(Fr.fromBuffer(buf));
}
}

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/circuits.js/src/tests/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,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,
);

Expand Down
4 changes: 1 addition & 3 deletions yarn-project/end-to-end/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,5 @@
"path": "../world-state"
}
],
"include": [
"src"
]
"include": ["src"]
}
1 change: 1 addition & 0 deletions yarn-project/foundation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions yarn-project/foundation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
48 changes: 48 additions & 0 deletions yarn-project/foundation/src/trees/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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(): IndexedTreeLeaf;
/**
* Serializes the preimage into a buffer.
*/
toBuffer(): Buffer;
/**
* Serializes the preimage to an array of buffers for hashing.
*/
toHashInputs(): Buffer[];
}
1 change: 1 addition & 0 deletions yarn-project/merkle-tree/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/merkle-tree/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export * from './interfaces/merkle_tree.js';
export * from './interfaces/update_only_tree.js';
export * from './pedersen.js';
export * from './sparse_tree/sparse_tree.js';
export { LowLeafWitnessData, StandardIndexedTree } from './standard_indexed_tree/standard_indexed_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 } from './new_tree.js';
Expand Down
55 changes: 38 additions & 17 deletions yarn-project/merkle-tree/src/interfaces/indexed_tree.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
import { LeafData, SiblingPath } from '@aztec/types';
import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
import { SiblingPath } from '@aztec/types';

import { LowLeafWitnessData } from '../index.js';
import { AppendOnlyTree } from './append_only_tree.js';

/**
* All of the data to be return during batch insertion.
*/
export interface LowLeafWitnessData<N extends number> {
/**
* Preimage of the low nullifier that proves non membership.
*/
leafPreimage: IndexedTreeLeafPreimage;
/**
* Sibling path to prove membership of low nullifier.
*/
siblingPath: SiblingPath<N>;
/**
* The index of low nullifier.
*/
index: bigint;
}

/**
* The result of a batch insertion in an indexed merkle tree.
*/
Expand Down Expand Up @@ -35,27 +53,30 @@ export interface IndexedTree extends AppendOnlyTree {
* @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,
): {
/**
* The index of the found leaf.
*/
index: number;
/**
* 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
>;

/**
* 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: bigint, includeUncommitted: boolean): Promise<IndexedTreeLeafPreimage | undefined>;

/**
* Batch insert multiple leaves into the tree.
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/merkle-tree/src/interfaces/merkle_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Buffer | undefined>;

/**
* 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<bigint | undefined>;
}
5 changes: 1 addition & 4 deletions yarn-project/merkle-tree/src/interfaces/update_only_tree.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { LeafData } from '@aztec/types';

import { TreeSnapshotBuilder } from '../snapshots/snapshot_builder.js';
import { MerkleTree } from './merkle_tree.js';

Expand All @@ -12,6 +10,5 @@ export interface UpdateOnlyTree extends MerkleTree, TreeSnapshotBuilder {
* @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<void>;
updateLeaf(leaf: Buffer, index: bigint): Promise<void>;
}
Loading