@aztec/merkle-tree 0.31.0 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dest/hasher_with_stats.d.ts +1 -1
  2. package/dest/hasher_with_stats.d.ts.map +1 -1
  3. package/dest/interfaces/append_only_tree.d.ts +5 -4
  4. package/dest/interfaces/append_only_tree.d.ts.map +1 -1
  5. package/dest/interfaces/indexed_tree.d.ts +6 -4
  6. package/dest/interfaces/indexed_tree.d.ts.map +1 -1
  7. package/dest/interfaces/merkle_tree.d.ts +6 -5
  8. package/dest/interfaces/merkle_tree.d.ts.map +1 -1
  9. package/dest/interfaces/update_only_tree.d.ts +5 -4
  10. package/dest/interfaces/update_only_tree.d.ts.map +1 -1
  11. package/dest/load_tree.d.ts +5 -4
  12. package/dest/load_tree.d.ts.map +1 -1
  13. package/dest/load_tree.js +3 -3
  14. package/dest/new_tree.d.ts +5 -4
  15. package/dest/new_tree.d.ts.map +1 -1
  16. package/dest/new_tree.js +3 -3
  17. package/dest/pedersen.d.ts +1 -1
  18. package/dest/pedersen.d.ts.map +1 -1
  19. package/dest/sha_256.d.ts +1 -1
  20. package/dest/sha_256.d.ts.map +1 -1
  21. package/dest/snapshots/append_only_snapshot.d.ts +11 -9
  22. package/dest/snapshots/append_only_snapshot.d.ts.map +1 -1
  23. package/dest/snapshots/append_only_snapshot.js +13 -8
  24. package/dest/snapshots/base_full_snapshot.d.ts +12 -10
  25. package/dest/snapshots/base_full_snapshot.d.ts.map +1 -1
  26. package/dest/snapshots/base_full_snapshot.js +7 -4
  27. package/dest/snapshots/full_snapshot.d.ts +8 -4
  28. package/dest/snapshots/full_snapshot.d.ts.map +1 -1
  29. package/dest/snapshots/full_snapshot.js +6 -2
  30. package/dest/snapshots/indexed_tree_snapshot.d.ts +6 -6
  31. package/dest/snapshots/indexed_tree_snapshot.d.ts.map +1 -1
  32. package/dest/snapshots/indexed_tree_snapshot.js +2 -2
  33. package/dest/snapshots/snapshot_builder.d.ts +9 -8
  34. package/dest/snapshots/snapshot_builder.d.ts.map +1 -1
  35. package/dest/snapshots/snapshot_builder_test_suite.d.ts +4 -3
  36. package/dest/snapshots/snapshot_builder_test_suite.d.ts.map +1 -1
  37. package/dest/snapshots/snapshot_builder_test_suite.js +5 -2
  38. package/dest/sparse_tree/sparse_tree.d.ts +9 -9
  39. package/dest/sparse_tree/sparse_tree.d.ts.map +1 -1
  40. package/dest/sparse_tree/sparse_tree.js +6 -4
  41. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +8 -6
  42. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -1
  43. package/dest/standard_indexed_tree/standard_indexed_tree.js +5 -2
  44. package/dest/standard_tree/standard_tree.d.ts +9 -8
  45. package/dest/standard_tree/standard_tree.d.ts.map +1 -1
  46. package/dest/standard_tree/standard_tree.js +5 -3
  47. package/dest/tree_base.d.ts +19 -10
  48. package/dest/tree_base.d.ts.map +1 -1
  49. package/dest/tree_base.js +20 -3
  50. package/package.json +5 -5
  51. package/src/hasher_with_stats.ts +1 -1
  52. package/src/interfaces/append_only_tree.ts +8 -4
  53. package/src/interfaces/indexed_tree.ts +13 -4
  54. package/src/interfaces/merkle_tree.ts +6 -5
  55. package/src/interfaces/update_only_tree.ts +8 -4
  56. package/src/load_tree.ts +16 -6
  57. package/src/new_tree.ts +8 -6
  58. package/src/pedersen.ts +1 -1
  59. package/src/sha_256.ts +1 -1
  60. package/src/snapshots/append_only_snapshot.ts +28 -16
  61. package/src/snapshots/base_full_snapshot.ts +14 -11
  62. package/src/snapshots/full_snapshot.ts +14 -7
  63. package/src/snapshots/indexed_tree_snapshot.ts +10 -10
  64. package/src/snapshots/snapshot_builder.ts +9 -8
  65. package/src/snapshots/snapshot_builder_test_suite.ts +14 -8
  66. package/src/sparse_tree/sparse_tree.ts +13 -10
  67. package/src/standard_indexed_tree/standard_indexed_tree.ts +19 -9
  68. package/src/standard_tree/standard_tree.ts +13 -11
  69. package/src/tree_base.ts +27 -10
@@ -1,6 +1,9 @@
1
- import { TreeBase } from '../tree_base.js';
1
+ import { type Bufferable, type FromBuffer } from '@aztec/foundation/serialize';
2
+ import { type AztecKVStore } from '@aztec/kv-store';
3
+
4
+ import { type TreeBase } from '../tree_base.js';
2
5
  import { BaseFullTreeSnapshot, BaseFullTreeSnapshotBuilder } from './base_full_snapshot.js';
3
- import { TreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js';
6
+ import { type TreeSnapshot, type TreeSnapshotBuilder } from './snapshot_builder.js';
4
7
 
5
8
  /**
6
9
  * Builds a full snapshot of a tree. This implementation works for any Merkle tree and stores
@@ -16,11 +19,15 @@ import { TreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js';
16
19
  * Worst case space complexity: O(N * M)
17
20
  * Sibling path access: O(H) database reads
18
21
  */
19
- export class FullTreeSnapshotBuilder
20
- extends BaseFullTreeSnapshotBuilder<TreeBase, TreeSnapshot>
21
- implements TreeSnapshotBuilder<TreeSnapshot>
22
+ export class FullTreeSnapshotBuilder<T extends Bufferable>
23
+ extends BaseFullTreeSnapshotBuilder<TreeBase<T>, TreeSnapshot<T>>
24
+ implements TreeSnapshotBuilder<TreeSnapshot<T>>
22
25
  {
23
- protected openSnapshot(root: Buffer, numLeaves: bigint): TreeSnapshot {
24
- return new BaseFullTreeSnapshot(this.nodes, root, numLeaves, this.tree);
26
+ constructor(db: AztecKVStore, tree: TreeBase<T>, private deserializer: FromBuffer<T>) {
27
+ super(db, tree);
28
+ }
29
+
30
+ protected openSnapshot(root: Buffer, numLeaves: bigint): TreeSnapshot<T> {
31
+ return new BaseFullTreeSnapshot(this.nodes, root, numLeaves, this.tree, this.deserializer);
25
32
  }
26
33
  }
@@ -1,20 +1,20 @@
1
- import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
2
- import { AztecKVStore, AztecMap } from '@aztec/kv-store';
1
+ import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
2
+ import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
3
3
 
4
- import { IndexedTree, PreimageFactory } from '../interfaces/indexed_tree.js';
5
- import { TreeBase } from '../tree_base.js';
4
+ import { type IndexedTree, type PreimageFactory } from '../interfaces/indexed_tree.js';
5
+ import { type TreeBase } from '../tree_base.js';
6
6
  import { BaseFullTreeSnapshot, BaseFullTreeSnapshotBuilder } from './base_full_snapshot.js';
7
- import { IndexedTreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js';
7
+ import { type IndexedTreeSnapshot, type TreeSnapshotBuilder } from './snapshot_builder.js';
8
8
 
9
9
  const snapshotLeafValue = (node: Buffer, index: bigint) => 'snapshot:leaf:' + node.toString('hex') + ':' + index;
10
10
 
11
11
  /** a */
12
12
  export class IndexedTreeSnapshotBuilder
13
- extends BaseFullTreeSnapshotBuilder<IndexedTree & TreeBase, IndexedTreeSnapshot>
13
+ extends BaseFullTreeSnapshotBuilder<IndexedTree & TreeBase<Buffer>, IndexedTreeSnapshot>
14
14
  implements TreeSnapshotBuilder<IndexedTreeSnapshot>
15
15
  {
16
16
  leaves: AztecMap<string, Buffer>;
17
- constructor(store: AztecKVStore, tree: IndexedTree & TreeBase, private leafPreimageBuilder: PreimageFactory) {
17
+ constructor(store: AztecKVStore, tree: IndexedTree & TreeBase<Buffer>, private leafPreimageBuilder: PreimageFactory) {
18
18
  super(store, tree);
19
19
  this.leaves = store.openMap('indexed_tree_snapshot:' + tree.getName());
20
20
  }
@@ -32,16 +32,16 @@ export class IndexedTreeSnapshotBuilder
32
32
  }
33
33
 
34
34
  /** A snapshot of an indexed tree at a particular point in time */
35
- class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTreeSnapshot {
35
+ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot<Buffer> implements IndexedTreeSnapshot {
36
36
  constructor(
37
37
  db: AztecMap<string, [Buffer, Buffer]>,
38
38
  private leaves: AztecMap<string, Buffer>,
39
39
  historicRoot: Buffer,
40
40
  numLeaves: bigint,
41
- tree: IndexedTree & TreeBase,
41
+ tree: IndexedTree & TreeBase<Buffer>,
42
42
  private leafPreimageBuilder: PreimageFactory,
43
43
  ) {
44
- super(db, historicRoot, numLeaves, tree);
44
+ super(db, historicRoot, numLeaves, tree, { fromBuffer: buf => buf });
45
45
  }
46
46
 
47
47
  getLeafValue(index: bigint): Buffer | undefined {
@@ -1,10 +1,11 @@
1
- import { SiblingPath } from '@aztec/circuit-types';
2
- import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
1
+ import { type SiblingPath } from '@aztec/circuit-types';
2
+ import { type Bufferable } from '@aztec/foundation/serialize';
3
+ import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
3
4
 
4
5
  /**
5
6
  * An interface for a tree that can record snapshots of its contents.
6
7
  */
7
- export interface TreeSnapshotBuilder<S extends TreeSnapshot = TreeSnapshot> {
8
+ export interface TreeSnapshotBuilder<S extends TreeSnapshot<Bufferable>> {
8
9
  /**
9
10
  * Creates a snapshot of the tree at the given version.
10
11
  * @param block - The version to snapshot the tree at.
@@ -21,7 +22,7 @@ export interface TreeSnapshotBuilder<S extends TreeSnapshot = TreeSnapshot> {
21
22
  /**
22
23
  * A tree snapshot
23
24
  */
24
- export interface TreeSnapshot {
25
+ export interface TreeSnapshot<T extends Bufferable> {
25
26
  /**
26
27
  * Returns the current root of the tree.
27
28
  */
@@ -41,7 +42,7 @@ export interface TreeSnapshot {
41
42
  * Returns the value of a leaf at the specified index.
42
43
  * @param index - The index of the leaf value to be returned.
43
44
  */
44
- getLeafValue(index: bigint): Buffer | undefined;
45
+ getLeafValue(index: bigint): T | undefined;
45
46
 
46
47
  /**
47
48
  * Returns the sibling path for a requested leaf index.
@@ -55,7 +56,7 @@ export interface TreeSnapshot {
55
56
  * @param value - The leaf value to look for.
56
57
  * @returns The index of the first leaf found with a given value (undefined if not found).
57
58
  */
58
- findLeafIndex(value: Buffer): bigint | undefined;
59
+ findLeafIndex(value: T): bigint | undefined;
59
60
 
60
61
  /**
61
62
  * Returns the first index containing a leaf value after `startIndex`.
@@ -63,11 +64,11 @@ export interface TreeSnapshot {
63
64
  * @param startIndex - The index to start searching from (used when skipping nullified messages)
64
65
  * @returns The index of the first leaf found with a given value (undefined if not found).
65
66
  */
66
- findLeafIndexAfter(leaf: Buffer, startIndex: bigint): bigint | undefined;
67
+ findLeafIndexAfter(leaf: T, startIndex: bigint): bigint | undefined;
67
68
  }
68
69
 
69
70
  /** A snapshot of an indexed tree */
70
- export interface IndexedTreeSnapshot extends TreeSnapshot {
71
+ export interface IndexedTreeSnapshot extends TreeSnapshot<Buffer> {
71
72
  /**
72
73
  * Gets the historical data for a leaf
73
74
  * @param index - The index of the leaf to get the data for
@@ -1,14 +1,18 @@
1
1
  import { randomBigInt } from '@aztec/foundation/crypto';
2
+ import { type Bufferable } from '@aztec/foundation/serialize';
2
3
 
3
- import { TreeBase } from '../tree_base.js';
4
- import { TreeSnapshotBuilder } from './snapshot_builder.js';
4
+ import { jest } from '@jest/globals';
5
+
6
+ import { type TreeBase } from '../tree_base.js';
7
+ import { type TreeSnapshot, type TreeSnapshotBuilder } from './snapshot_builder.js';
8
+
9
+ jest.setTimeout(50_000);
5
10
 
6
11
  /** Creates a test suit for snapshots */
7
- export function describeSnapshotBuilderTestSuite<T extends TreeBase, S extends TreeSnapshotBuilder>(
8
- getTree: () => T,
9
- getSnapshotBuilder: () => S,
10
- modifyTree: (tree: T) => Promise<void>,
11
- ) {
12
+ export function describeSnapshotBuilderTestSuite<
13
+ T extends TreeBase<Bufferable>,
14
+ S extends TreeSnapshotBuilder<TreeSnapshot<Bufferable>>,
15
+ >(getTree: () => T, getSnapshotBuilder: () => S, modifyTree: (tree: T) => Promise<void>) {
12
16
  describe('SnapshotBuilder', () => {
13
17
  let tree: T;
14
18
  let snapshotBuilder: S;
@@ -34,7 +38,9 @@ export function describeSnapshotBuilderTestSuite<T extends TreeBase, S extends T
34
38
 
35
39
  const block = 1;
36
40
  const snapshot = await snapshotBuilder.snapshot(block);
37
- await expect(snapshotBuilder.snapshot(block)).resolves.toEqual(snapshot);
41
+ const newSnapshot = await snapshotBuilder.snapshot(block);
42
+
43
+ expect(newSnapshot.getRoot()).toEqual(snapshot.getRoot());
38
44
  });
39
45
 
40
46
  it('returns the same path if tree has not diverged', async () => {
@@ -1,25 +1,28 @@
1
- import { UpdateOnlyTree } from '../interfaces/update_only_tree.js';
1
+ import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize';
2
+
3
+ import { type UpdateOnlyTree } from '../interfaces/update_only_tree.js';
2
4
  import { FullTreeSnapshotBuilder } from '../snapshots/full_snapshot.js';
3
- import { TreeSnapshot } from '../snapshots/snapshot_builder.js';
5
+ import { type TreeSnapshot } from '../snapshots/snapshot_builder.js';
4
6
  import { INITIAL_LEAF, TreeBase } from '../tree_base.js';
5
7
 
6
8
  /**
7
9
  * A Merkle tree implementation that uses a LevelDB database to store the tree.
8
10
  */
9
- export class SparseTree extends TreeBase implements UpdateOnlyTree {
10
- #snapshotBuilder = new FullTreeSnapshotBuilder(this.store, this);
11
+ export class SparseTree<T extends Bufferable> extends TreeBase<T> implements UpdateOnlyTree<T> {
12
+ #snapshotBuilder = new FullTreeSnapshotBuilder(this.store, this, this.deserializer);
11
13
  /**
12
14
  * Updates a leaf in the tree.
13
15
  * @param leaf - New contents of the leaf.
14
16
  * @param index - Index of the leaf to be updated.
15
17
  */
16
- public updateLeaf(leaf: Buffer, index: bigint): Promise<void> {
18
+ public updateLeaf(value: T, index: bigint): Promise<void> {
17
19
  if (index > this.maxIndex) {
18
20
  throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`);
19
21
  }
20
22
 
23
+ const leaf = serializeToBuffer(value);
21
24
  const insertingZeroElement = leaf.equals(INITIAL_LEAF);
22
- const originallyZeroElement = this.getLeafValue(index, true)?.equals(INITIAL_LEAF);
25
+ const originallyZeroElement = this.getLeafBuffer(index, true)?.equals(INITIAL_LEAF);
23
26
  if (insertingZeroElement && originallyZeroElement) {
24
27
  return Promise.resolve();
25
28
  }
@@ -35,19 +38,19 @@ export class SparseTree extends TreeBase implements UpdateOnlyTree {
35
38
  return Promise.resolve();
36
39
  }
37
40
 
38
- public snapshot(block: number): Promise<TreeSnapshot> {
41
+ public snapshot(block: number): Promise<TreeSnapshot<T>> {
39
42
  return this.#snapshotBuilder.snapshot(block);
40
43
  }
41
44
 
42
- public getSnapshot(block: number): Promise<TreeSnapshot> {
45
+ public getSnapshot(block: number): Promise<TreeSnapshot<T>> {
43
46
  return this.#snapshotBuilder.getSnapshot(block);
44
47
  }
45
48
 
46
- public findLeafIndex(_value: Buffer, _includeUncommitted: boolean): bigint | undefined {
49
+ public findLeafIndex(_value: T, _includeUncommitted: boolean): bigint | undefined {
47
50
  throw new Error('Finding leaf index is not supported for sparse trees');
48
51
  }
49
52
 
50
- public findLeafIndexAfter(_value: Buffer, _startIndex: bigint, _includeUncommitted: boolean): bigint | undefined {
53
+ public findLeafIndexAfter(_value: T, _startIndex: bigint, _includeUncommitted: boolean): bigint | undefined {
51
54
  throw new Error('Finding leaf index is not supported for sparse trees');
52
55
  }
53
56
  }
@@ -1,14 +1,20 @@
1
1
  import { SiblingPath } from '@aztec/circuit-types';
2
- import { TreeInsertionStats } from '@aztec/circuit-types/stats';
2
+ import { type TreeInsertionStats } from '@aztec/circuit-types/stats';
3
3
  import { toBufferBE } from '@aztec/foundation/bigint-buffer';
4
+ import { type FromBuffer } from '@aztec/foundation/serialize';
4
5
  import { Timer } from '@aztec/foundation/timer';
5
- import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
6
- import { AztecKVStore, AztecMap } from '@aztec/kv-store';
7
- import { Hasher } from '@aztec/types/interfaces';
8
-
9
- import { BatchInsertionResult, IndexedTree, LowLeafWitnessData, PreimageFactory } from '../interfaces/indexed_tree.js';
6
+ import { type IndexedTreeLeaf, type IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
7
+ import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
8
+ import { type Hasher } from '@aztec/types/interfaces';
9
+
10
+ import {
11
+ type BatchInsertionResult,
12
+ type IndexedTree,
13
+ type LowLeafWitnessData,
14
+ type PreimageFactory,
15
+ } from '../interfaces/indexed_tree.js';
10
16
  import { IndexedTreeSnapshotBuilder } from '../snapshots/indexed_tree_snapshot.js';
11
- import { IndexedTreeSnapshot } from '../snapshots/snapshot_builder.js';
17
+ import { type IndexedTreeSnapshot } from '../snapshots/snapshot_builder.js';
12
18
  import { TreeBase } from '../tree_base.js';
13
19
 
14
20
  export const buildDbKeyForPreimage = (name: string, index: bigint) => {
@@ -51,10 +57,14 @@ function getEmptyLowLeafWitness<N extends number>(
51
57
  };
52
58
  }
53
59
 
60
+ export const noopDeserializer: FromBuffer<Buffer> = {
61
+ fromBuffer: (buf: Buffer) => buf,
62
+ };
63
+
54
64
  /**
55
65
  * Standard implementation of an indexed tree.
56
66
  */
57
- export class StandardIndexedTree extends TreeBase implements IndexedTree {
67
+ export class StandardIndexedTree extends TreeBase<Buffer> implements IndexedTree {
58
68
  #snapshotBuilder = new IndexedTreeSnapshotBuilder(this.store, this, this.leafPreimageFactory);
59
69
 
60
70
  protected cachedLeafPreimages: { [key: string]: IndexedTreeLeafPreimage } = {};
@@ -71,7 +81,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
71
81
  protected leafFactory: LeafFactory,
72
82
  root?: Buffer,
73
83
  ) {
74
- super(store, hasher, name, depth, size, root);
84
+ super(store, hasher, name, depth, size, noopDeserializer, root);
75
85
  this.leaves = store.openMap(`tree_${name}_leaves`);
76
86
  this.leafIndex = store.openMap(`tree_${name}_leaf_index`);
77
87
  }
@@ -1,23 +1,24 @@
1
- import { TreeInsertionStats } from '@aztec/circuit-types/stats';
1
+ import { type TreeInsertionStats } from '@aztec/circuit-types/stats';
2
+ import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize';
2
3
  import { Timer } from '@aztec/foundation/timer';
3
4
 
4
- import { AppendOnlyTree } from '../interfaces/append_only_tree.js';
5
+ import { type AppendOnlyTree } from '../interfaces/append_only_tree.js';
5
6
  import { AppendOnlySnapshotBuilder } from '../snapshots/append_only_snapshot.js';
6
- import { TreeSnapshot } from '../snapshots/snapshot_builder.js';
7
+ import { type TreeSnapshot } from '../snapshots/snapshot_builder.js';
7
8
  import { TreeBase } from '../tree_base.js';
8
9
 
9
10
  /**
10
11
  * A Merkle tree implementation that uses a LevelDB database to store the tree.
11
12
  */
12
- export class StandardTree extends TreeBase implements AppendOnlyTree {
13
- #snapshotBuilder = new AppendOnlySnapshotBuilder(this.store, this, this.hasher);
13
+ export class StandardTree<T extends Bufferable = Buffer> extends TreeBase<T> implements AppendOnlyTree<T> {
14
+ #snapshotBuilder = new AppendOnlySnapshotBuilder(this.store, this, this.hasher, this.deserializer);
14
15
 
15
16
  /**
16
17
  * Appends the given leaves to the tree.
17
18
  * @param leaves - The leaves to append.
18
19
  * @returns Empty promise.
19
20
  */
20
- public appendLeaves(leaves: Buffer[]): Promise<void> {
21
+ public appendLeaves(leaves: T[]): Promise<void> {
21
22
  this.hasher.reset();
22
23
  const timer = new Timer();
23
24
  super.appendLeaves(leaves);
@@ -34,22 +35,23 @@ export class StandardTree extends TreeBase implements AppendOnlyTree {
34
35
  return Promise.resolve();
35
36
  }
36
37
 
37
- public snapshot(blockNumber: number): Promise<TreeSnapshot> {
38
+ public snapshot(blockNumber: number): Promise<TreeSnapshot<T>> {
38
39
  return this.#snapshotBuilder.snapshot(blockNumber);
39
40
  }
40
41
 
41
- public getSnapshot(blockNumber: number): Promise<TreeSnapshot> {
42
+ public getSnapshot(blockNumber: number): Promise<TreeSnapshot<T>> {
42
43
  return this.#snapshotBuilder.getSnapshot(blockNumber);
43
44
  }
44
45
 
45
- public findLeafIndex(value: Buffer, includeUncommitted: boolean): bigint | undefined {
46
+ public findLeafIndex(value: T, includeUncommitted: boolean): bigint | undefined {
46
47
  return this.findLeafIndexAfter(value, 0n, includeUncommitted);
47
48
  }
48
49
 
49
- public findLeafIndexAfter(value: Buffer, startIndex: bigint, includeUncommitted: boolean): bigint | undefined {
50
+ public findLeafIndexAfter(value: T, startIndex: bigint, includeUncommitted: boolean): bigint | undefined {
51
+ const buffer = serializeToBuffer(value);
50
52
  for (let i = startIndex; i < this.getNumLeaves(includeUncommitted); i++) {
51
53
  const currentValue = this.getLeafValue(i, includeUncommitted);
52
- if (currentValue && currentValue.equals(value)) {
54
+ if (currentValue && serializeToBuffer(currentValue).equals(buffer)) {
53
55
  return i;
54
56
  }
55
57
  }
package/src/tree_base.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { SiblingPath } from '@aztec/circuit-types';
2
2
  import { toBigIntLE, toBufferLE } from '@aztec/foundation/bigint-buffer';
3
- import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
4
- import { AztecKVStore, AztecMap, AztecSingleton } from '@aztec/kv-store';
5
- import { Hasher } from '@aztec/types/interfaces';
3
+ import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
4
+ import { type Bufferable, type FromBuffer, serializeToBuffer } from '@aztec/foundation/serialize';
5
+ import { type AztecKVStore, type AztecMap, type AztecSingleton } from '@aztec/kv-store';
6
+ import { type Hasher } from '@aztec/types/interfaces';
6
7
 
7
8
  import { HasherWithStats } from './hasher_with_stats.js';
8
- import { MerkleTree } from './interfaces/merkle_tree.js';
9
+ import { type MerkleTree } from './interfaces/merkle_tree.js';
9
10
 
10
11
  const MAX_DEPTH = 254;
11
12
 
@@ -44,7 +45,7 @@ export const INITIAL_LEAF = Buffer.from('000000000000000000000000000000000000000
44
45
  /**
45
46
  * A Merkle tree implementation that uses a LevelDB database to store the tree.
46
47
  */
47
- export abstract class TreeBase implements MerkleTree {
48
+ export abstract class TreeBase<T extends Bufferable> implements MerkleTree<T> {
48
49
  protected readonly maxIndex: bigint;
49
50
  protected cachedSize?: bigint;
50
51
  private root!: Buffer;
@@ -62,6 +63,7 @@ export abstract class TreeBase implements MerkleTree {
62
63
  private name: string,
63
64
  private depth: number,
64
65
  protected size: bigint = 0n,
66
+ protected deserializer: FromBuffer<T>,
65
67
  root?: Buffer,
66
68
  ) {
67
69
  if (!(depth >= 1 && depth <= MAX_DEPTH)) {
@@ -173,7 +175,22 @@ export abstract class TreeBase implements MerkleTree {
173
175
  * @param includeUncommitted - Indicates whether to include uncommitted changes.
174
176
  * @returns Leaf value at the given index or undefined.
175
177
  */
176
- public getLeafValue(index: bigint, includeUncommitted: boolean): Buffer | undefined {
178
+ public getLeafValue(index: bigint, includeUncommitted: boolean): T | undefined {
179
+ const buf = this.getLatestValueAtIndex(this.depth, index, includeUncommitted);
180
+ if (buf) {
181
+ return this.deserializer.fromBuffer(buf);
182
+ } else {
183
+ return undefined;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Gets the value at the given index.
189
+ * @param index - The index of the leaf.
190
+ * @param includeUncommitted - Indicates whether to include uncommitted changes.
191
+ * @returns Leaf value at the given index or undefined.
192
+ */
193
+ public getLeafBuffer(index: bigint, includeUncommitted: boolean): Buffer | undefined {
177
194
  return this.getLatestValueAtIndex(this.depth, index, includeUncommitted);
178
195
  }
179
196
 
@@ -292,7 +309,7 @@ export abstract class TreeBase implements MerkleTree {
292
309
  * `getLatestValueAtIndex` will return a value from cache (because at least one of the 2 children was
293
310
  * touched in previous iteration).
294
311
  */
295
- protected appendLeaves(leaves: Buffer[]): void {
312
+ protected appendLeaves(leaves: T[]): void {
296
313
  const numLeaves = this.getNumLeaves(true);
297
314
  if (numLeaves + BigInt(leaves.length) - 1n > this.maxIndex) {
298
315
  throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
@@ -303,7 +320,7 @@ export abstract class TreeBase implements MerkleTree {
303
320
  let level = this.depth;
304
321
  for (let i = 0; i < leaves.length; i++) {
305
322
  const cacheKey = indexToKeyHash(this.name, level, firstIndex + BigInt(i));
306
- this.cache[cacheKey] = leaves[i];
323
+ this.cache[cacheKey] = serializeToBuffer(leaves[i]);
307
324
  }
308
325
 
309
326
  let lastIndex = firstIndex + BigInt(leaves.length);
@@ -330,7 +347,7 @@ export abstract class TreeBase implements MerkleTree {
330
347
  * @param includeUncommitted - Indicates whether to include uncommitted data.
331
348
  * @returns The index of the first leaf found with a given value (undefined if not found).
332
349
  */
333
- abstract findLeafIndex(value: Buffer, includeUncommitted: boolean): bigint | undefined;
350
+ abstract findLeafIndex(value: T, includeUncommitted: boolean): bigint | undefined;
334
351
 
335
352
  /**
336
353
  * Returns the first index containing a leaf value after `startIndex`.
@@ -339,5 +356,5 @@ export abstract class TreeBase implements MerkleTree {
339
356
  * @param includeUncommitted - Indicates whether to include uncommitted data.
340
357
  * @returns The index of the first leaf found with a given value (undefined if not found).
341
358
  */
342
- abstract findLeafIndexAfter(leaf: Buffer, startIndex: bigint, includeUncommitted: boolean): bigint | undefined;
359
+ abstract findLeafIndexAfter(leaf: T, startIndex: bigint, includeUncommitted: boolean): bigint | undefined;
343
360
  }