@aztec/merkle-tree 0.1.0-alpha10

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 (94) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/.tsbuildinfo +1 -0
  3. package/README.md +41 -0
  4. package/dest/hasher.d.ts +11 -0
  5. package/dest/hasher.d.ts.map +1 -0
  6. package/dest/hasher.js +2 -0
  7. package/dest/index.d.ts +14 -0
  8. package/dest/index.d.ts.map +1 -0
  9. package/dest/index.js +14 -0
  10. package/dest/interfaces/append_only_tree.d.ts +13 -0
  11. package/dest/interfaces/append_only_tree.d.ts.map +1 -0
  12. package/dest/interfaces/append_only_tree.js +2 -0
  13. package/dest/interfaces/indexed_tree.d.ts +63 -0
  14. package/dest/interfaces/indexed_tree.d.ts.map +1 -0
  15. package/dest/interfaces/indexed_tree.js +2 -0
  16. package/dest/interfaces/merkle_tree.d.ts +47 -0
  17. package/dest/interfaces/merkle_tree.d.ts.map +1 -0
  18. package/dest/interfaces/merkle_tree.js +2 -0
  19. package/dest/interfaces/update_only_tree.d.ts +15 -0
  20. package/dest/interfaces/update_only_tree.d.ts.map +1 -0
  21. package/dest/interfaces/update_only_tree.js +2 -0
  22. package/dest/load_tree.d.ts +13 -0
  23. package/dest/load_tree.d.ts.map +1 -0
  24. package/dest/load_tree.js +17 -0
  25. package/dest/new_tree.d.ts +15 -0
  26. package/dest/new_tree.d.ts.map +1 -0
  27. package/dest/new_tree.js +16 -0
  28. package/dest/pedersen.d.ts +42 -0
  29. package/dest/pedersen.d.ts.map +1 -0
  30. package/dest/pedersen.js +49 -0
  31. package/dest/sibling_path/sibling_path.d.ts +92 -0
  32. package/dest/sibling_path/sibling_path.d.ts.map +1 -0
  33. package/dest/sibling_path/sibling_path.js +120 -0
  34. package/dest/sparse_tree/sparse_tree.d.ts +15 -0
  35. package/dest/sparse_tree/sparse_tree.d.ts.map +1 -0
  36. package/dest/sparse_tree/sparse_tree.js +31 -0
  37. package/dest/sparse_tree/sparse_tree.test.d.ts +2 -0
  38. package/dest/sparse_tree/sparse_tree.test.d.ts.map +1 -0
  39. package/dest/sparse_tree/sparse_tree.test.js +132 -0
  40. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +230 -0
  41. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -0
  42. package/dest/standard_indexed_tree/standard_indexed_tree.js +497 -0
  43. package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts +2 -0
  44. package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts.map +1 -0
  45. package/dest/standard_indexed_tree/standard_indexed_tree.test.js +316 -0
  46. package/dest/standard_tree/standard_tree.d.ts +25 -0
  47. package/dest/standard_tree/standard_tree.d.ts.map +1 -0
  48. package/dest/standard_tree/standard_tree.js +50 -0
  49. package/dest/standard_tree/standard_tree.test.d.ts +2 -0
  50. package/dest/standard_tree/standard_tree.test.d.ts.map +1 -0
  51. package/dest/standard_tree/standard_tree.test.js +58 -0
  52. package/dest/test/standard_based_test_suite.d.ts +6 -0
  53. package/dest/test/standard_based_test_suite.d.ts.map +1 -0
  54. package/dest/test/standard_based_test_suite.js +86 -0
  55. package/dest/test/test_suite.d.ts +6 -0
  56. package/dest/test/test_suite.d.ts.map +1 -0
  57. package/dest/test/test_suite.js +118 -0
  58. package/dest/test/utils/append_leaves.d.ts +5 -0
  59. package/dest/test/utils/append_leaves.d.ts.map +1 -0
  60. package/dest/test/utils/append_leaves.js +14 -0
  61. package/dest/test/utils/create_mem_down.d.ts +3 -0
  62. package/dest/test/utils/create_mem_down.d.ts.map +1 -0
  63. package/dest/test/utils/create_mem_down.js +3 -0
  64. package/dest/test/utils/pedersen_with_counter.d.ts +24 -0
  65. package/dest/test/utils/pedersen_with_counter.d.ts.map +1 -0
  66. package/dest/test/utils/pedersen_with_counter.js +31 -0
  67. package/dest/tree_base.d.ts +118 -0
  68. package/dest/tree_base.d.ts.map +1 -0
  69. package/dest/tree_base.js +214 -0
  70. package/package.json +14 -0
  71. package/package.local.json +3 -0
  72. package/src/hasher.ts +9 -0
  73. package/src/index.ts +13 -0
  74. package/src/interfaces/append_only_tree.ts +12 -0
  75. package/src/interfaces/indexed_tree.ts +78 -0
  76. package/src/interfaces/merkle_tree.ts +52 -0
  77. package/src/interfaces/update_only_tree.ts +15 -0
  78. package/src/load_tree.ts +24 -0
  79. package/src/new_tree.ts +26 -0
  80. package/src/pedersen.ts +58 -0
  81. package/src/sibling_path/sibling_path.ts +139 -0
  82. package/src/sparse_tree/sparse_tree.test.ts +177 -0
  83. package/src/sparse_tree/sparse_tree.ts +32 -0
  84. package/src/standard_indexed_tree/standard_indexed_tree.test.ts +450 -0
  85. package/src/standard_indexed_tree/standard_indexed_tree.ts +591 -0
  86. package/src/standard_tree/standard_tree.test.ts +74 -0
  87. package/src/standard_tree/standard_tree.ts +54 -0
  88. package/src/test/standard_based_test_suite.ts +139 -0
  89. package/src/test/test_suite.ts +162 -0
  90. package/src/test/utils/append_leaves.ts +15 -0
  91. package/src/test/utils/create_mem_down.ts +3 -0
  92. package/src/test/utils/pedersen_with_counter.ts +30 -0
  93. package/src/tree_base.ts +242 -0
  94. package/tsconfig.json +17 -0
@@ -0,0 +1,214 @@
1
+ import { SiblingPath } from './sibling_path/sibling_path.js';
2
+ import { toBigIntLE, toBufferLE } from '@aztec/foundation/bigint-buffer';
3
+ const MAX_DEPTH = 254;
4
+ export const indexToKeyHash = (name, level, index) => `${name}:${level}:${index}`;
5
+ const encodeMeta = (root, depth, size) => {
6
+ const data = Buffer.alloc(36);
7
+ root.copy(data);
8
+ data.writeUInt32LE(depth, 32);
9
+ return Buffer.concat([data, toBufferLE(size, 32)]);
10
+ };
11
+ export const decodeMeta = (meta) => {
12
+ const root = meta.subarray(0, 32);
13
+ const depth = meta.readUInt32LE(32);
14
+ const size = toBigIntLE(meta.subarray(36));
15
+ return {
16
+ root,
17
+ depth,
18
+ size,
19
+ };
20
+ };
21
+ export const INITIAL_LEAF = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex');
22
+ /**
23
+ * A Merkle tree implementation that uses a LevelDB database to store the tree.
24
+ */
25
+ export class TreeBase {
26
+ constructor(db, hasher, name, depth, size = 0n, root) {
27
+ this.db = db;
28
+ this.hasher = hasher;
29
+ this.name = name;
30
+ this.depth = depth;
31
+ this.size = size;
32
+ this.zeroHashes = [];
33
+ this.cache = {};
34
+ if (!(depth >= 1 && depth <= MAX_DEPTH)) {
35
+ throw Error('Invalid depth');
36
+ }
37
+ // Compute the zero values at each layer.
38
+ let current = INITIAL_LEAF;
39
+ for (let i = depth - 1; i >= 0; --i) {
40
+ this.zeroHashes[i] = current;
41
+ current = hasher.compress(current, current);
42
+ }
43
+ this.root = root ? root : current;
44
+ this.maxIndex = 2n ** BigInt(depth) - 1n;
45
+ }
46
+ /**
47
+ * Returns the root of the tree.
48
+ * @param includeUncommitted - If true, root incorporating uncomitted changes is returned.
49
+ * @returns The root of the tree.
50
+ */
51
+ getRoot(includeUncommitted) {
52
+ return !includeUncommitted ? this.root : this.cache[indexToKeyHash(this.name, 0, 0n)] ?? this.root;
53
+ }
54
+ /**
55
+ * Returns the number of leaves in the tree.
56
+ * @param includeUncommitted - If true, the returned number of leaves includes uncomitted changes.
57
+ * @returns The number of leaves in the tree.
58
+ */
59
+ getNumLeaves(includeUncommitted) {
60
+ return !includeUncommitted ? this.size : this.cachedSize ?? this.size;
61
+ }
62
+ /**
63
+ * Returns the name of the tree.
64
+ * @returns The name of the tree.
65
+ */
66
+ getName() {
67
+ return this.name;
68
+ }
69
+ /**
70
+ * Returns the depth of the tree.
71
+ * @returns The depth of the tree.
72
+ */
73
+ getDepth() {
74
+ return this.depth;
75
+ }
76
+ /**
77
+ * Returns a sibling path for the element at the given index.
78
+ * @param index - The index of the element.
79
+ * @param includeUncommitted - Indicates whether to get a sibling path incorporating uncommitted changes.
80
+ * @returns A sibling path for the element at the given index.
81
+ * Note: The sibling path is an array of sibling hashes, with the lowest hash (leaf hash) first, and the highest hash last.
82
+ */
83
+ async getSiblingPath(index, includeUncommitted) {
84
+ const path = [];
85
+ let level = this.depth;
86
+ while (level > 0) {
87
+ const isRight = index & 0x01n;
88
+ const sibling = await this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, includeUncommitted);
89
+ path.push(sibling);
90
+ level -= 1;
91
+ index >>= 1n;
92
+ }
93
+ return new SiblingPath(this.depth, path);
94
+ }
95
+ /**
96
+ * Commits the changes to the database.
97
+ * @returns Empty promise.
98
+ */
99
+ async commit() {
100
+ const batch = this.db.batch();
101
+ const keys = Object.getOwnPropertyNames(this.cache);
102
+ for (const key of keys) {
103
+ batch.put(key, this.cache[key]);
104
+ }
105
+ this.size = this.getNumLeaves(true);
106
+ this.root = this.getRoot(true);
107
+ await this.writeMeta(batch);
108
+ await batch.write();
109
+ this.clearCache();
110
+ }
111
+ /**
112
+ * Rolls back the not-yet-committed changes.
113
+ * @returns Empty promise.
114
+ */
115
+ rollback() {
116
+ this.clearCache();
117
+ return Promise.resolve();
118
+ }
119
+ /**
120
+ * Gets the value at the given index.
121
+ * @param index - The index of the leaf.
122
+ * @param includeUncommitted - Indicates whether to include uncommitted changes.
123
+ * @returns Leaf value at the given index or undefined.
124
+ */
125
+ getLeafValue(index, includeUncommitted) {
126
+ return this.getLatestValueAtIndex(this.depth, index, includeUncommitted);
127
+ }
128
+ /**
129
+ * Clears the cache.
130
+ */
131
+ clearCache() {
132
+ this.cache = {};
133
+ this.cachedSize = undefined;
134
+ }
135
+ /**
136
+ * Adds a leaf and all the hashes above it to the cache.
137
+ * @param leaf - Leaf to add to cache.
138
+ * @param index - Index of the leaf (used to derive the cache key).
139
+ */
140
+ async addLeafToCacheAndHashToRoot(leaf, index) {
141
+ const key = indexToKeyHash(this.name, this.depth, index);
142
+ let current = leaf;
143
+ this.cache[key] = current;
144
+ let level = this.depth;
145
+ while (level > 0) {
146
+ const isRight = index & 0x01n;
147
+ const sibling = await this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, true);
148
+ const lhs = isRight ? sibling : current;
149
+ const rhs = isRight ? current : sibling;
150
+ current = this.hasher.compress(lhs, rhs);
151
+ level -= 1;
152
+ index >>= 1n;
153
+ const cacheKey = indexToKeyHash(this.name, level, index);
154
+ this.cache[cacheKey] = current;
155
+ }
156
+ }
157
+ /**
158
+ * Returns the latest value at the given index.
159
+ * @param level - The level of the tree.
160
+ * @param index - The index of the element.
161
+ * @param includeUncommitted - Indicates, whether to get include uncommitted changes.
162
+ * @returns The latest value at the given index.
163
+ * Note: If the value is not in the cache, it will be fetched from the database.
164
+ */
165
+ async getLatestValueAtIndex(level, index, includeUncommitted) {
166
+ const key = indexToKeyHash(this.name, level, index);
167
+ if (includeUncommitted && this.cache[key] !== undefined) {
168
+ return this.cache[key];
169
+ }
170
+ const committed = await this.dbGet(key);
171
+ if (committed !== undefined) {
172
+ return committed;
173
+ }
174
+ return this.zeroHashes[level - 1];
175
+ }
176
+ /**
177
+ * Gets a value from db by key.
178
+ * @param key - The key to by which to get the value.
179
+ * @returns A value from the db based on the key.
180
+ */
181
+ async dbGet(key) {
182
+ return await this.db.get(key).catch(() => { });
183
+ }
184
+ /**
185
+ * Initializes the tree.
186
+ * @param prefilledSize - A number of leaves that are prefilled with values.
187
+ * @returns Empty promise.
188
+ */
189
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
190
+ async init(prefilledSize) {
191
+ // prefilledSize is used only by Indexed Tree.
192
+ await this.writeMeta();
193
+ }
194
+ /**
195
+ * Initializes the tree from the database.
196
+ */
197
+ async initFromDb() {
198
+ // Implemented only by Inedexed Tree to populate the leaf cache.
199
+ }
200
+ /**
201
+ * Writes meta data to the provided batch.
202
+ * @param batch - The batch to which to write the meta data.
203
+ */
204
+ async writeMeta(batch) {
205
+ const data = encodeMeta(this.getRoot(true), this.depth, this.getNumLeaves(true));
206
+ if (batch) {
207
+ batch.put(this.name, data);
208
+ }
209
+ else {
210
+ await this.db.put(this.name, data);
211
+ }
212
+ }
213
+ }
214
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZV9iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3RyZWVfYmFzZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFHN0QsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUV6RSxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUM7QUFFdEIsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsSUFBWSxFQUFFLEtBQWEsRUFBRSxLQUFhLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxJQUFJLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztBQUMxRyxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBRSxLQUFhLEVBQUUsSUFBWSxFQUFFLEVBQUU7SUFDL0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM5QixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hCLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzlCLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDLENBQUM7QUFDRixNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRTtJQUN6QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNsQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxD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
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@aztec/merkle-tree",
3
+ "version": "0.1.0-alpha10",
4
+ "exports": "./dest/index.js",
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@aztec/circuits.js": "0.1.0-alpha10",
8
+ "@aztec/foundation": "0.1.0-alpha10",
9
+ "levelup": "^5.1.1",
10
+ "memdown": "^6.1.1",
11
+ "sha256": "^0.2.0",
12
+ "tslib": "^2.4.0"
13
+ }
14
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "files": ["dest", "src", "!*.test.*", "!dest/test", "!src/test"]
3
+ }
package/src/hasher.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Defines hasher interface used by Merkle trees.
3
+ */
4
+ export interface Hasher {
5
+ compress(lhs: Uint8Array, rhs: Uint8Array): Buffer;
6
+ compressInputs(inputs: Buffer[]): Buffer;
7
+ hashToField(data: Uint8Array): Buffer;
8
+ hashToTree(leaves: Buffer[]): Promise<Buffer[]>;
9
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export * from './hasher.js';
2
+ export * from './interfaces/append_only_tree.js';
3
+ export * from './interfaces/indexed_tree.js';
4
+ export * from './interfaces/merkle_tree.js';
5
+ export * from './interfaces/update_only_tree.js';
6
+ export * from './pedersen.js';
7
+ export * from './sibling_path/sibling_path.js';
8
+ export * from './sparse_tree/sparse_tree.js';
9
+ export * from './standard_indexed_tree/standard_indexed_tree.js';
10
+ export * from './standard_tree/standard_tree.js';
11
+ export { INITIAL_LEAF } from './tree_base.js';
12
+ export { newTree } from './new_tree.js';
13
+ export { loadTree } from './load_tree.js';
@@ -0,0 +1,12 @@
1
+ import { MerkleTree } from './merkle_tree.js';
2
+
3
+ /**
4
+ * A Merkle tree that supports only appending leaves and not updating existing leaves.
5
+ */
6
+ export interface AppendOnlyTree extends MerkleTree {
7
+ /**
8
+ * Appends a set of leaf values to the tree.
9
+ * @param leaves - The set of leaves to be appended.
10
+ */
11
+ appendLeaves(leaves: Buffer[]): Promise<void>;
12
+ }
@@ -0,0 +1,78 @@
1
+ import { LowLeafWitnessData, SiblingPath } from '../index.js';
2
+ import { AppendOnlyTree } from './append_only_tree.js';
3
+
4
+ /**
5
+ * A leaf of a tree.
6
+ */
7
+ export interface LeafData {
8
+ /**
9
+ * A value of the leaf.
10
+ */
11
+ value: bigint;
12
+ /**
13
+ * An index of the next leaf.
14
+ */
15
+ nextIndex: bigint;
16
+ /**
17
+ * A value of the next leaf.
18
+ */
19
+ nextValue: bigint;
20
+ }
21
+
22
+ /**
23
+ * Indexed merkle tree.
24
+ */
25
+ export interface IndexedTree extends AppendOnlyTree {
26
+ /**
27
+ * Finds the index of the largest leaf whose value is less than or equal to the provided value.
28
+ * @param newValue - The new value to be inserted into the tree.
29
+ * @param includeUncommitted - If true, the uncommitted changes are included in the search.
30
+ * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`.
31
+ */
32
+ findIndexOfPreviousValue(
33
+ newValue: bigint,
34
+ includeUncommitted: boolean,
35
+ ): {
36
+ /**
37
+ * The index of the found leaf.
38
+ */
39
+ index: number;
40
+ /**
41
+ * A flag indicating if the corresponding leaf's value is equal to `newValue`.
42
+ */
43
+ alreadyPresent: boolean;
44
+ };
45
+
46
+ /**
47
+ * Gets the latest LeafData copy.
48
+ * @param index - Index of the leaf of which to obtain the LeafData copy.
49
+ * @param includeUncommitted - If true, the uncommitted changes are included in the search.
50
+ * @returns A copy of the leaf data at the given index or undefined if the leaf was not found.
51
+ */
52
+ getLatestLeafDataCopy(index: number, includeUncommitted: boolean): LeafData | undefined;
53
+
54
+ /**
55
+ * Exposes the underlying tree's update leaf method.
56
+ * @param leaf - The hash to set at the leaf.
57
+ * @param index - The index of the element.
58
+ */
59
+ // TODO: remove once the batch insertion functionality is moved to StandardIndexedTree from circuit_block_builder.ts
60
+ updateLeaf(leaf: LeafData, index: bigint): Promise<void>;
61
+
62
+ /**
63
+ * Batch insert multiple leaves into the tree.
64
+ * @param leaves - Leaves to insert into the tree.
65
+ * @param treeHeight - Height of the tree.
66
+ * @param subtreeHeight - Height of the subtree.
67
+ * @param includeUncommitted - If true, the uncommitted changes are included in the search.
68
+ */
69
+ batchInsert<TreeHeight extends number, SubtreeHeight extends number, SubtreeSiblingPathHeight extends number>(
70
+ leaves: Buffer[],
71
+ treeHeight: TreeHeight,
72
+ subtreeHeight: SubtreeHeight,
73
+ includeUncommitted: boolean,
74
+ ): Promise<
75
+ | [LowLeafWitnessData<TreeHeight>[], SiblingPath<SubtreeSiblingPathHeight>]
76
+ | [undefined, SiblingPath<SubtreeSiblingPathHeight>]
77
+ >;
78
+ }
@@ -0,0 +1,52 @@
1
+ import { SiblingPath } from '../sibling_path/sibling_path.js';
2
+
3
+ /**
4
+ * Defines the interface for a source of sibling paths.
5
+ */
6
+ export interface SiblingPathSource {
7
+ /**
8
+ * Returns the sibling path for a requested leaf index.
9
+ * @param index - The index of the leaf for which a sibling path is required.
10
+ * @param includeUncommitted - Set to true to include uncommitted updates in the sibling path.
11
+ */
12
+ getSiblingPath<N extends number>(index: bigint, includeUncommitted: boolean): Promise<SiblingPath<N>>;
13
+ }
14
+
15
+ /**
16
+ * Defines the interface for a Merkle tree.
17
+ */
18
+ export interface MerkleTree extends SiblingPathSource {
19
+ /**
20
+ * Returns the current root of the tree.
21
+ * @param includeUncommitted - Set to true to include uncommitted updates in the calculated root.
22
+ */
23
+ getRoot(includeUncommitted: boolean): Buffer;
24
+
25
+ /**
26
+ * Returns the number of leaves in the tree.
27
+ * @param includeUncommitted - Set to true to include uncommitted updates in the returned value.
28
+ */
29
+ getNumLeaves(includeUncommitted: boolean): bigint;
30
+
31
+ /**
32
+ * Commit pending updates to the tree.
33
+ */
34
+ commit(): Promise<void>;
35
+
36
+ /**
37
+ * Returns the depth of the tree.
38
+ */
39
+ getDepth(): number;
40
+
41
+ /**
42
+ * Rollback pending update to the tree.
43
+ */
44
+ rollback(): Promise<void>;
45
+
46
+ /**
47
+ * Returns the value of a leaf at the specified index.
48
+ * @param index - The index of the leaf value to be returned.
49
+ * @param includeUncommitted - Set to true to include uncommitted updates in the data set.
50
+ */
51
+ getLeafValue(index: bigint, includeUncommitted: boolean): Promise<Buffer | undefined>;
52
+ }
@@ -0,0 +1,15 @@
1
+ import { LeafData } from '../index.js';
2
+ import { MerkleTree } from './merkle_tree.js';
3
+
4
+ /**
5
+ * A Merkle tree that supports updates at arbitrary indices but not appending.
6
+ */
7
+ export interface UpdateOnlyTree extends MerkleTree {
8
+ /**
9
+ * Updates a leaf at a given index in the tree.
10
+ * @param leaf - The leaf value to be updated.
11
+ * @param index - The leaf to be updated.
12
+ */
13
+ // TODO: Make this strictly a Buffer
14
+ updateLeaf(leaf: Buffer | LeafData, index: bigint): Promise<void>;
15
+ }
@@ -0,0 +1,24 @@
1
+ import { LevelUp } from 'levelup';
2
+ import { Hasher } from './hasher.js';
3
+ import { TreeBase, decodeMeta } from './tree_base.js';
4
+
5
+ /**
6
+ * Creates a new tree and sets its root, depth and size based on the meta data which are associated with the name.
7
+ * @param c - The class of the tree to be instantiated.
8
+ * @param db - A database used to store the Merkle tree data.
9
+ * @param hasher - A hasher used to compute hash paths.
10
+ * @param name - Name of the tree.
11
+ * @returns The newly created tree.
12
+ */
13
+ export async function loadTree<T extends TreeBase>(
14
+ c: new (...args: any[]) => T,
15
+ db: LevelUp,
16
+ hasher: Hasher,
17
+ name: string,
18
+ ): Promise<T> {
19
+ const meta: Buffer = await db.get(name);
20
+ const { root, depth, size } = decodeMeta(meta);
21
+ const tree = new c(db, hasher, name, depth, size, root);
22
+ await tree.initFromDb();
23
+ return tree;
24
+ }
@@ -0,0 +1,26 @@
1
+ import { LevelUp } from 'levelup';
2
+ import { Hasher } from './hasher.js';
3
+ import { TreeBase } from './tree_base.js';
4
+
5
+ /**
6
+ * Creates a new tree.
7
+ * @param c - The class of the tree to be instantiated.
8
+ * @param db - A database used to store the Merkle tree data.
9
+ * @param hasher - A hasher used to compute hash paths.
10
+ * @param name - Name of the tree.
11
+ * @param depth - Depth of the tree.
12
+ * @param prefilledSize - A number of leaves that are prefilled with values.
13
+ * @returns The newly created tree.
14
+ */
15
+ export async function newTree<T extends TreeBase>(
16
+ c: new (...args: any[]) => T,
17
+ db: LevelUp,
18
+ hasher: Hasher,
19
+ name: string,
20
+ depth: number,
21
+ prefilledSize = 0,
22
+ ): Promise<T> {
23
+ const tree = new c(db, hasher, name, depth, 0n, undefined);
24
+ await tree.init(prefilledSize);
25
+ return tree;
26
+ }
@@ -0,0 +1,58 @@
1
+ import { IWasmModule } from '@aztec/foundation/wasm';
2
+ import {
3
+ pedersenCompress,
4
+ pedersenGetHash,
5
+ pedersenGetHashTree,
6
+ pedersenHashInputs,
7
+ } from '@aztec/circuits.js/barretenberg';
8
+ import { Hasher } from './hasher.js';
9
+
10
+ /**
11
+ * A helper class encapsulating Pedersen hash functionality.
12
+ */
13
+ export class Pedersen implements Hasher {
14
+ constructor(private wasm: IWasmModule) {}
15
+
16
+ /**
17
+ * Compresses two 32-byte hashes.
18
+ * @param lhs - The first hash.
19
+ * @param rhs - The second hash.
20
+ * @returns The new 32-byte hash.
21
+ */
22
+ public compress(lhs: Uint8Array, rhs: Uint8Array): Buffer {
23
+ return pedersenCompress(this.wasm, lhs, rhs);
24
+ }
25
+
26
+ /**
27
+ * Compresses an array of buffers.
28
+ * @param inputs - The array of buffers to compress.
29
+ * @returns The resulting 32-byte hash.
30
+ */
31
+ public compressInputs(inputs: Buffer[]): Buffer {
32
+ return pedersenHashInputs(this.wasm, inputs);
33
+ }
34
+
35
+ /**
36
+ * Get a 32-byte pedersen hash from a buffer.
37
+ * @param data - The data buffer.
38
+ * @returns The resulting hash buffer.
39
+ */
40
+ public hashToField(data: Uint8Array): Buffer {
41
+ return pedersenGetHash(this.wasm, Buffer.from(data));
42
+ }
43
+
44
+ /**
45
+ * Given a buffer containing 32 byte pedersen leaves, return a new buffer containing the leaves and all pairs of
46
+ * nodes that define a merkle tree.
47
+ *
48
+ * E.g.
49
+ * Input: [1][2][3][4]
50
+ * Output: [1][2][3][4][compress(1,2)][compress(3,4)][compress(5,6)].
51
+ *
52
+ * @param leaves - The 32 byte pedersen leaves.
53
+ * @returns A tree represented by an array.
54
+ */
55
+ public hashToTree(leaves: Buffer[]): Promise<Buffer[]> {
56
+ return Promise.resolve(pedersenGetHashTree(this.wasm, leaves));
57
+ }
58
+ }
@@ -0,0 +1,139 @@
1
+ import {
2
+ Tuple,
3
+ assertLength,
4
+ deserializeArrayFromVector,
5
+ serializeBufferArrayToVector,
6
+ } from '@aztec/foundation/serialize';
7
+ import { Pedersen } from '../pedersen.js';
8
+ import { Fr } from '@aztec/foundation/fields';
9
+
10
+ /**
11
+ * Contains functionality to compute and serialize/deserialize a sibling path.
12
+ * E.g. Sibling path for a leaf at index 3 in a tree of depth 3 consists of:
13
+ * d0: [ root ]
14
+ * d1: [ ] [*]
15
+ * d2: [*] [ ] [ ] [ ]
16
+ * d3: [ ] [ ] [*] [ ] [ ] [ ] [ ] [ ].
17
+ *
18
+ * And the elements would be ordered as: [ leaf_at_index_2, node_at_level_2_index_0, node_at_level_1_index_1 ].
19
+ */
20
+ export class SiblingPath<N extends number> {
21
+ private data: Tuple<Buffer, N>;
22
+
23
+ /**
24
+ * Returns sibling path hashed up from the a element.
25
+ * @param size - The number of elements in a given path.
26
+ * @param zeroElement - Value of the zero element.
27
+ * @param pedersen - Implementation of a hasher interface using the Pedersen hash.
28
+ * @returns A sibling path hashed up from a zero element.
29
+ */
30
+ public static ZERO<N extends number>(size: N, zeroElement: Buffer, pedersen: Pedersen): SiblingPath<N> {
31
+ const bufs: Buffer[] = [];
32
+ let current = zeroElement;
33
+ for (let i = 0; i < size; ++i) {
34
+ bufs.push(current);
35
+ current = pedersen.compress(current, current);
36
+ }
37
+ return new SiblingPath(size, bufs);
38
+ }
39
+
40
+ /**
41
+ * Constructor.
42
+ * @param pathSize - The size of the sibling path.
43
+ * @param path - The sibling path data.
44
+ */
45
+ constructor(
46
+ /**
47
+ * Size of the sibling path (number of fields it contains).
48
+ */
49
+ public pathSize: N,
50
+ /**
51
+ * The sibling path data.
52
+ */
53
+ path: Buffer[],
54
+ ) {
55
+ this.data = assertLength(path, pathSize);
56
+ }
57
+
58
+ /**
59
+ * Serializes this SiblingPath object to a buffer.
60
+ * @returns The buffer representation of this object.
61
+ */
62
+ public toBuffer(): Buffer {
63
+ return serializeBufferArrayToVector(this.data);
64
+ }
65
+
66
+ /**
67
+ * Returns the path buffer underlying the sibling path.
68
+ * @returns The Buffer array representation of this object.
69
+ */
70
+ public toBufferArray(): Buffer[] {
71
+ return this.data;
72
+ }
73
+
74
+ /**
75
+ * Convert the Sibling Path object into an array of field elements.
76
+ * @returns The field array representation of this object.
77
+ */
78
+ public toFieldArray(): Fr[] {
79
+ return this.data.map(buf => Fr.fromBuffer(buf));
80
+ }
81
+
82
+ /**
83
+ * Deserializes a SiblingPath from a buffer.
84
+ * @param buf - A buffer containing the buffer representation of SiblingPath.
85
+ * @param offset - An offset to start deserializing from.
86
+ * @returns A SiblingPath object.
87
+ */
88
+ static fromBuffer<N extends number>(buf: Buffer, offset = 0): SiblingPath<N> {
89
+ const { elem } = SiblingPath.deserialize<N>(buf, offset);
90
+ return elem;
91
+ }
92
+
93
+ /**
94
+ * Deserializes a SiblingPath object from a slice of a part of a buffer and returns the amount of bytes advanced.
95
+ * @param buf - A buffer representation of the sibling path.
96
+ * @param offset - An offset to start deserializing from.
97
+ * @returns The deserialized sibling path and the number of bytes advanced.
98
+ */
99
+ static deserialize<N extends number>(buf: Buffer, offset = 0) {
100
+ const deserializePath = (buf: Buffer, offset: number) => ({
101
+ elem: buf.slice(offset, offset + 32),
102
+ adv: 32,
103
+ });
104
+ const { elem, adv } = deserializeArrayFromVector(deserializePath, buf, offset);
105
+ const size = elem.length;
106
+ return { elem: new SiblingPath<N>(size as N, elem), adv };
107
+ }
108
+
109
+ /**
110
+ * Serializes this SiblingPath object to a hex string representation.
111
+ * @returns A hex string representation of the sibling path.
112
+ */
113
+ public toString(): string {
114
+ return this.toBuffer().toString('hex');
115
+ }
116
+
117
+ /**
118
+ * Deserializes a SiblingPath object from a hex string representation.
119
+ * @param repr - A hex string representation of the sibling path.
120
+ * @returns A SiblingPath object.
121
+ */
122
+ public static fromString<N extends number>(repr: string): SiblingPath<N> {
123
+ return SiblingPath.fromBuffer<N>(Buffer.from(repr, 'hex'));
124
+ }
125
+
126
+ /**
127
+ * Generate a subtree path from the current sibling path.
128
+ * @param subtreeHeight - The size of the subtree that we are getting the path for.
129
+ * @returns A new sibling path that is the for the requested subtree.
130
+ */
131
+ public getSubtreeSiblingPath<SubtreeHeight extends number, SubtreeSiblingPathHeight extends number>(
132
+ subtreeHeight: SubtreeHeight,
133
+ ): SiblingPath<SubtreeSiblingPathHeight> {
134
+ // Drop the size of the subtree from the path, and return the rest.
135
+ const subtreeData = this.data.slice(subtreeHeight);
136
+ const subtreePathSize = (this.pathSize - subtreeHeight) as SubtreeSiblingPathHeight;
137
+ return new SiblingPath(subtreePathSize, subtreeData);
138
+ }
139
+ }