@aztec/merkle-tree 0.1.0-alpha17 → 0.1.0-alpha21

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 (29) hide show
  1. package/.tsbuildinfo +1 -1
  2. package/dest/interfaces/indexed_tree.d.ts +1 -8
  3. package/dest/interfaces/indexed_tree.d.ts.map +1 -1
  4. package/dest/new_tree.js +1 -1
  5. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +37 -22
  6. package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -1
  7. package/dest/standard_indexed_tree/standard_indexed_tree.js +78 -87
  8. package/dest/standard_indexed_tree/test/standard_indexed_tree.test.d.ts.map +1 -0
  9. package/dest/standard_indexed_tree/test/standard_indexed_tree.test.js +337 -0
  10. package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.d.ts +23 -0
  11. package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.d.ts.map +1 -0
  12. package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.js +59 -0
  13. package/dest/standard_tree/standard_tree.d.ts +0 -10
  14. package/dest/standard_tree/standard_tree.d.ts.map +1 -1
  15. package/dest/standard_tree/standard_tree.js +3 -38
  16. package/dest/tree_base.d.ts +22 -9
  17. package/dest/tree_base.d.ts.map +1 -1
  18. package/dest/tree_base.js +48 -5
  19. package/package.json +3 -3
  20. package/src/interfaces/indexed_tree.ts +0 -10
  21. package/src/new_tree.ts +1 -1
  22. package/src/standard_indexed_tree/standard_indexed_tree.ts +86 -96
  23. package/src/standard_indexed_tree/{standard_indexed_tree.test.ts → test/standard_indexed_tree.test.ts} +35 -9
  24. package/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +63 -0
  25. package/src/standard_tree/standard_tree.ts +2 -40
  26. package/src/tree_base.ts +55 -8
  27. package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts.map +0 -1
  28. package/dest/standard_indexed_tree/standard_indexed_tree.test.js +0 -316
  29. /package/dest/standard_indexed_tree/{standard_indexed_tree.test.d.ts → test/standard_indexed_tree.test.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tree_base.d.ts","sourceRoot":"","sources":["../src/tree_base.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAKzD,eAAO,MAAM,cAAc,SAAU,MAAM,SAAS,MAAM,SAAS,MAAM,WAAgC,CAAC;AAO1G,eAAO,MAAM,UAAU,SAAU,MAAM;;;;CAStC,CAAC;AAEF,eAAO,MAAM,YAAY,QAAyF,CAAC;AAEnH;;GAEG;AACH,8BAAsB,QAAS,YAAW,UAAU;IAQhD,SAAS,CAAC,EAAE,EAAE,OAAO;IACrB,SAAS,CAAC,MAAM,EAAE,MAAM;IACxB,SAAS,CAAC,IAAI,EAAE,MAAM;IACtB,SAAS,CAAC,KAAK,EAAE,MAAM;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM;IAXxB,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IACpC,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,UAAU,CAAgB;IAClC,SAAS,CAAC,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;gBAGpC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAW,EAC3B,IAAI,CAAC,EAAE,MAAM;IAiBf;;;;OAIG;IACI,OAAO,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM;IAInD;;;;OAIG;IACI,YAAY,CAAC,kBAAkB,EAAE,OAAO;IAI/C;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;OAGG;IACI,QAAQ,IAAI,MAAM;IAIzB;;;;;;OAMG;IACU,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAalH;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAapC;;;OAGG;IACI,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKhC;;;;;OAKG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI5F;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;;;OAIG;cACa,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAkBvE;;;;;;;OAOG;cACa,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAYjH;;;;OAIG;YACW,KAAK;IAInB;;;;OAIG;IAEU,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC;;;OAGG;cACa,SAAS,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC;CAQ/D"}
1
+ {"version":3,"file":"tree_base.d.ts","sourceRoot":"","sources":["../src/tree_base.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAYzD,eAAO,MAAM,UAAU,SAAU,MAAM;;;;CAStC,CAAC;AAEF,eAAO,MAAM,YAAY,QAAyF,CAAC;AAEnH;;GAEG;AACH,8BAAsB,QAAS,YAAW,UAAU;IAQhD,SAAS,CAAC,EAAE,EAAE,OAAO;IACrB,SAAS,CAAC,MAAM,EAAE,MAAM;IACxB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,KAAK;IACb,SAAS,CAAC,IAAI,EAAE,MAAM;IAXxB,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IACpC,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,KAAK,CAAiC;gBAGlC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACX,IAAI,GAAE,MAAW,EAC3B,IAAI,CAAC,EAAE,MAAM;IAiBf;;;;OAIG;IACI,OAAO,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM;IAInD;;;;OAIG;IACI,YAAY,CAAC,kBAAkB,EAAE,OAAO;IAI/C;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;OAGG;IACI,QAAQ,IAAI,MAAM;IAIzB;;;;;;OAMG;IACU,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAalH;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAapC;;;OAGG;IACI,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKhC;;;;;OAKG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI5F;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;;;OAIG;cACa,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAkBvE;;;;;;;OAOG;YACW,qBAAqB;IAYnC;;;;OAIG;YACW,KAAK;IAInB;;;;OAIG;IAEU,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC;;;OAGG;cACa,SAAS,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC;IAS9D;;;;;;;;;;;;;;OAcG;cACa,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA+B9D"}
package/dest/tree_base.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SiblingPath } from './sibling_path/sibling_path.js';
2
2
  import { toBigIntLE, toBufferLE } from '@aztec/foundation/bigint-buffer';
3
3
  const MAX_DEPTH = 254;
4
- export const indexToKeyHash = (name, level, index) => `${name}:${level}:${index}`;
4
+ const indexToKeyHash = (name, level, index) => `${name}:${level}:${index}`;
5
5
  const encodeMeta = (root, depth, size) => {
6
6
  const data = Buffer.alloc(36);
7
7
  root.copy(data);
@@ -45,7 +45,7 @@ export class TreeBase {
45
45
  }
46
46
  /**
47
47
  * Returns the root of the tree.
48
- * @param includeUncommitted - If true, root incorporating uncomitted changes is returned.
48
+ * @param includeUncommitted - If true, root incorporating uncommitted changes is returned.
49
49
  * @returns The root of the tree.
50
50
  */
51
51
  getRoot(includeUncommitted) {
@@ -53,7 +53,7 @@ export class TreeBase {
53
53
  }
54
54
  /**
55
55
  * Returns the number of leaves in the tree.
56
- * @param includeUncommitted - If true, the returned number of leaves includes uncomitted changes.
56
+ * @param includeUncommitted - If true, the returned number of leaves includes uncommitted changes.
57
57
  * @returns The number of leaves in the tree.
58
58
  */
59
59
  getNumLeaves(includeUncommitted) {
@@ -195,7 +195,7 @@ export class TreeBase {
195
195
  * Initializes the tree from the database.
196
196
  */
197
197
  async initFromDb() {
198
- // Implemented only by Inedexed Tree to populate the leaf cache.
198
+ // Implemented only by Indexed Tree to populate the leaf cache.
199
199
  }
200
200
  /**
201
201
  * Writes meta data to the provided batch.
@@ -210,5 +210,48 @@ export class TreeBase {
210
210
  await this.db.put(this.name, data);
211
211
  }
212
212
  }
213
+ /**
214
+ * Appends the given leaves to the tree.
215
+ * @param leaves - The leaves to append.
216
+ * @returns Empty promise.
217
+ *
218
+ * @remarks The batch insertion algorithm works as follows:
219
+ * 1. Insert all the leaves,
220
+ * 2. start iterating over levels from the bottom up,
221
+ * 3. on each level iterate over all the affected nodes (i.e. nodes whose preimages have changed),
222
+ * 4. fetch the preimage, hash it and insert the updated value.
223
+ * @remarks This algorithm is optimal when it comes to the number of hashing operations. It might not be optimal when
224
+ * it comes to the number of database reads, but that should be irrelevant given that most of the time
225
+ * `getLatestValueAtIndex` will return a value from cache (because at least one of the 2 children was
226
+ * touched in previous iteration).
227
+ */
228
+ async appendLeaves(leaves) {
229
+ const numLeaves = this.getNumLeaves(true);
230
+ if (numLeaves + BigInt(leaves.length) - 1n > this.maxIndex) {
231
+ throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
232
+ }
233
+ // 1. Insert all the leaves
234
+ let firstIndex = numLeaves;
235
+ let level = this.depth;
236
+ for (let i = 0; i < leaves.length; i++) {
237
+ const cacheKey = indexToKeyHash(this.name, level, firstIndex + BigInt(i));
238
+ this.cache[cacheKey] = leaves[i];
239
+ }
240
+ let lastIndex = firstIndex + BigInt(leaves.length);
241
+ // 2. Iterate over all the levels from the bottom up
242
+ while (level > 0) {
243
+ firstIndex >>= 1n;
244
+ lastIndex >>= 1n;
245
+ // 3.Iterate over all the affected nodes at this level and update them
246
+ for (let index = firstIndex; index <= lastIndex; index++) {
247
+ const lhs = await this.getLatestValueAtIndex(level, index * 2n, true);
248
+ const rhs = await this.getLatestValueAtIndex(level, index * 2n + 1n, true);
249
+ const cacheKey = indexToKeyHash(this.name, level - 1, index);
250
+ this.cache[cacheKey] = this.hasher.compress(lhs, rhs);
251
+ }
252
+ level -= 1;
253
+ }
254
+ this.cachedSize = numLeaves + BigInt(leaves.length);
255
+ }
213
256
  }
214
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZV9iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3RyZWVfYmFzZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFHN0QsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUV6RSxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUM7QUFFdEIsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsSUFBWSxFQUFFLEtBQWEsRUFBRSxLQUFhLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxJQUFJLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztBQUMxRyxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBRSxLQUFhLEVBQUUsSUFBWSxFQUFFLEVBQUU7SUFDL0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM5QixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hCLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzlCLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDLENBQUM7QUFDRixNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRTtJQUN6QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNsQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxD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
257
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZV9iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3RyZWVfYmFzZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFHN0QsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUV6RSxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUM7QUFFdEIsTUFBTSxjQUFjLEdBQUcsQ0FBQyxJQUFZLEVBQUUsS0FBYSxFQUFFLEtBQWEsRUFBRSxFQUFFLENBQUMsR0FBRyxJQUFJLElBQUksS0FBSyxJQUFJLEtBQUssRUFBRSxDQUFDO0FBQ25HLE1BQU0sVUFBVSxHQUFHLENBQUMsSUFBWSxFQUFFLEtBQWEsRUFBRSxJQUFZLEVBQUUsRUFBRTtJQUMvRCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDaEIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDOUIsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3JELENBQUMsQ0FBQztBQUNGLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO0lBQ3pDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ2xDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDcEMsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJ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LEtBQW9DO1FBQzVELE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ2pGLElBQUksS0FBSyxFQUFFO1lBQ1QsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO1NBQzVCO2FBQU07WUFDTCxNQUFNLElBQUksQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7U0FDcEM7SUFDSCxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDTyxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQWdCO1FBQzNDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDMUMsSUFBSSxTQUFTLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUMxRCxNQUFNLEtBQUssQ0FBQyw2Q0FBNkMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7U0FDM0U7UUFFRCwyQkFBMkI7UUFDM0IsSUFBSSxVQUFVLEdBQUcsU0FBUyxDQUFDO1FBQzNCLElBQUksS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDdkIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDdEMsTUFBTSxRQUFRLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUMxRSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNsQztRQUVELElBQUksU0FBUyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ25ELG9EQUFvRDtRQUNwRCxPQUFPLEtBQUssR0FBRyxDQUFDLEVBQUU7WUFDaEIsVUFBVSxLQUFLLEVBQUUsQ0FBQztZQUNsQixTQUFTLEtBQUssRUFBRSxDQUFDO1lBQ2pCLHNFQUFzRTtZQUN0RSxLQUFLLElBQUksS0FBSyxHQUFHLFVBQVUsRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLEtBQUssRUFBRSxFQUFFO2dCQUN4RCxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLEVBQUUsS0FBSyxHQUFHLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQztnQkFDdEUsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxFQUFFLEtBQUssR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUMzRSxNQUFNLFFBQVEsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUM3RCxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQzthQUN2RDtZQUVELEtBQUssSUFBSSxDQUFDLENBQUM7U0FDWjtRQUNELElBQUksQ0FBQyxVQUFVLEdBQUcsU0FBUyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsQ0FBQztDQUNGIn0=
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@aztec/merkle-tree",
3
- "version": "0.1.0-alpha17",
3
+ "version": "0.1.0-alpha21",
4
4
  "exports": "./dest/index.js",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@aztec/circuits.js": "0.1.0-alpha17",
8
- "@aztec/foundation": "0.1.0-alpha17",
7
+ "@aztec/circuits.js": "0.1.0-alpha21",
8
+ "@aztec/foundation": "0.1.0-alpha21",
9
9
  "levelup": "^5.1.1",
10
10
  "memdown": "^6.1.1",
11
11
  "sha256": "^0.2.0",
@@ -51,24 +51,14 @@ export interface IndexedTree extends AppendOnlyTree {
51
51
  */
52
52
  getLatestLeafDataCopy(index: number, includeUncommitted: boolean): LeafData | undefined;
53
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
54
  /**
63
55
  * Batch insert multiple leaves into the tree.
64
56
  * @param leaves - Leaves to insert into the tree.
65
- * @param treeHeight - Height of the tree.
66
57
  * @param subtreeHeight - Height of the subtree.
67
58
  * @param includeUncommitted - If true, the uncommitted changes are included in the search.
68
59
  */
69
60
  batchInsert<TreeHeight extends number, SubtreeHeight extends number, SubtreeSiblingPathHeight extends number>(
70
61
  leaves: Buffer[],
71
- treeHeight: TreeHeight,
72
62
  subtreeHeight: SubtreeHeight,
73
63
  includeUncommitted: boolean,
74
64
  ): Promise<
package/src/new_tree.ts CHANGED
@@ -18,7 +18,7 @@ export async function newTree<T extends TreeBase>(
18
18
  hasher: Hasher,
19
19
  name: string,
20
20
  depth: number,
21
- prefilledSize = 0,
21
+ prefilledSize = 1,
22
22
  ): Promise<T> {
23
23
  const tree = new c(db, hasher, name, depth, 0n, undefined);
24
24
  await tree.init(prefilledSize);
@@ -1,6 +1,5 @@
1
1
  import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
- import { Hasher } from '../hasher.js';
4
3
  import { IndexedTree, LeafData } from '../interfaces/indexed_tree.js';
5
4
  import { TreeBase } from '../tree_base.js';
6
5
  import { SiblingPath } from '../index.js';
@@ -56,10 +55,6 @@ const encodeTreeValue = (leafData: LeafData) => {
56
55
  return Buffer.concat([valueAsBuffer, indexAsBuffer, nextValueAsBuffer]);
57
56
  };
58
57
 
59
- const hashEncodedTreeValue = (leaf: LeafData, hasher: Hasher) => {
60
- return hasher.compressInputs([leaf.value, leaf.nextIndex, leaf.nextValue].map(val => toBufferBE(val, 32)));
61
- };
62
-
63
58
  const decodeTreeValue = (buf: Buffer) => {
64
59
  const value = toBigIntBE(buf.subarray(0, 32));
65
60
  const nextIndex = toBigIntBE(buf.subarray(32, 64));
@@ -71,28 +66,21 @@ const decodeTreeValue = (buf: Buffer) => {
71
66
  } as LeafData;
72
67
  };
73
68
 
74
- const initialLeaf: LeafData = {
75
- value: 0n,
76
- nextIndex: 0n,
77
- nextValue: 0n,
78
- };
79
-
80
69
  /**
81
70
  * Indexed merkle tree.
82
71
  */
83
72
  export class StandardIndexedTree extends TreeBase implements IndexedTree {
84
- private leaves: LeafData[] = [];
85
- private cachedLeaves: { [key: number]: LeafData } = {};
73
+ protected leaves: LeafData[] = [];
74
+ protected cachedLeaves: { [key: number]: LeafData } = {};
86
75
 
87
76
  /**
88
77
  * Appends the given leaves to the tree.
89
- * @param leaves - The leaves to append.
78
+ * @param _leaves - The leaves to append.
90
79
  * @returns Empty promise.
80
+ * @remarks Use batchInsert method instead.
91
81
  */
92
- public async appendLeaves(leaves: Buffer[]): Promise<void> {
93
- for (const leaf of leaves) {
94
- await this.appendLeaf(leaf);
95
- }
82
+ public appendLeaves(_leaves: Buffer[]): Promise<void> {
83
+ throw new Error('Not implemented');
96
84
  }
97
85
 
98
86
  /**
@@ -183,48 +171,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
183
171
  : undefined;
184
172
  }
185
173
 
186
- /**
187
- * Appends the given leaf to the tree.
188
- * @param leaf - The leaf to append.
189
- * @returns Empty promise.
190
- */
191
- private async appendLeaf(leaf: Buffer): Promise<void> {
192
- const newValue = toBigIntBE(leaf);
193
-
194
- // Special case when appending zero
195
- if (newValue === 0n) {
196
- const newSize = (this.cachedSize ?? this.size) + 1n;
197
- if (newSize - 1n > this.maxIndex) {
198
- throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
199
- }
200
- this.cachedSize = newSize;
201
- return;
202
- }
203
-
204
- const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true);
205
- const previousLeafCopy = this.getLatestLeafDataCopy(indexOfPrevious.index, true);
206
-
207
- if (previousLeafCopy === undefined) {
208
- throw new Error(`Previous leaf not found!`);
209
- }
210
- const newLeaf = {
211
- value: newValue,
212
- nextIndex: previousLeafCopy.nextIndex,
213
- nextValue: previousLeafCopy.nextValue,
214
- } as LeafData;
215
- if (indexOfPrevious.alreadyPresent) {
216
- return;
217
- }
218
- // insert a new leaf at the highest index and update the values of our previous leaf copy
219
- const currentSize = this.getNumLeaves(true);
220
- previousLeafCopy.nextIndex = BigInt(currentSize);
221
- previousLeafCopy.nextValue = newLeaf.value;
222
- this.cachedLeaves[Number(currentSize)] = newLeaf;
223
- this.cachedLeaves[Number(indexOfPrevious.index)] = previousLeafCopy;
224
- await this._updateLeaf(hashEncodedTreeValue(previousLeafCopy, this.hasher), BigInt(indexOfPrevious.index));
225
- await this._updateLeaf(hashEncodedTreeValue(newLeaf, this.hasher), this.getNumLeaves(true));
226
- }
227
-
228
174
  /**
229
175
  * Finds the index of the minimum value in an array.
230
176
  * @param values - The collection of values to be searched.
@@ -247,15 +193,39 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
247
193
  * Initializes the tree.
248
194
  * @param prefilledSize - A number of leaves that are prefilled with values.
249
195
  * @returns Empty promise.
196
+ *
197
+ * @remarks Explanation of pre-filling:
198
+ * There needs to be an initial (0,0,0) leaf in the tree, so that when we insert the first 'proper' leaf, we can
199
+ * prove that any value greater than 0 doesn't exist in the tree yet. We prefill/pad the tree with "the number of
200
+ * leaves that are added by one block" so that the first 'proper' block can insert a full subtree.
201
+ *
202
+ * Without this padding, there would be a leaf (0,0,0) at leaf index 0, making it really difficult to insert e.g.
203
+ * 1024 leaves for the first block, because there's only neat space for 1023 leaves after 0. By padding with 1023
204
+ * more leaves, we can then insert the first block of 1024 leaves into indices 1024:2047.
250
205
  */
251
206
  public async init(prefilledSize: number): Promise<void> {
252
- this.leaves.push(initialLeaf);
253
- await this._updateLeaf(hashEncodedTreeValue(initialLeaf, this.hasher), 0n);
207
+ if (prefilledSize < 1) {
208
+ throw new Error(`Prefilled size must be at least 1!`);
209
+ }
254
210
 
255
- for (let i = 1; i < prefilledSize; i++) {
256
- await this.appendLeaf(Buffer.from([i]));
211
+ const leaves: LeafData[] = [];
212
+ for (let i = 0n; i < prefilledSize; i++) {
213
+ const newLeaf = {
214
+ value: toBigIntBE(Buffer.from([Number(i)])),
215
+ nextIndex: i + 1n,
216
+ nextValue: i + 1n,
217
+ };
218
+ leaves.push(newLeaf);
257
219
  }
258
220
 
221
+ // Make the first leaf have 0 value
222
+ leaves[0].value = 0n;
223
+
224
+ // Make the last leaf point to the first leaf
225
+ leaves[prefilledSize - 1].nextIndex = 0n;
226
+ leaves[prefilledSize - 1].nextValue = 0n;
227
+
228
+ await this.encodeAndAppendLeaves(leaves, true);
259
229
  await this.commit();
260
230
  }
261
231
 
@@ -315,35 +285,19 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
315
285
  * @param leaf - New contents of the leaf.
316
286
  * @param index - Index of the leaf to be updated.
317
287
  */
318
- // TODO: rename back to updateLeaf once the old updateLeaf is removed
319
- private async _updateLeaf(leaf: Buffer, index: bigint) {
288
+ protected async updateLeaf(leaf: LeafData, index: bigint) {
320
289
  if (index > this.maxIndex) {
321
290
  throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`);
322
291
  }
323
- await this.addLeafToCacheAndHashToRoot(leaf, index);
292
+
293
+ const encodedLeaf = this.encodeLeaf(leaf, true);
294
+ await this.addLeafToCacheAndHashToRoot(encodedLeaf, index);
324
295
  const numLeaves = this.getNumLeaves(true);
325
296
  if (index >= numLeaves) {
326
297
  this.cachedSize = index + 1n;
327
298
  }
328
299
  }
329
300
 
330
- /**
331
- * Exposes the underlying tree's update leaf method.
332
- * @param leaf - The hash to set at the leaf.
333
- * @param index - The index of the element.
334
- */
335
- // TODO: remove once the batch insertion functionality is moved here from circuit_block_builder.ts
336
- public async updateLeaf(leaf: LeafData, index: bigint): Promise<void> {
337
- let encodedLeaf;
338
- if (leaf.value == 0n) {
339
- encodedLeaf = toBufferBE(0n, 32);
340
- } else {
341
- encodedLeaf = hashEncodedTreeValue(leaf, this.hasher);
342
- }
343
- this.cachedLeaves[Number(index)] = leaf;
344
- await this._updateLeaf(encodedLeaf, index);
345
- }
346
-
347
301
  /* eslint-disable jsdoc/require-description-complete-sentence */
348
302
  /* The following doc block messes up with complete-sentence, so we just disable it */
349
303
 
@@ -366,8 +320,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
366
320
  *
367
321
  * Algorithm overview
368
322
  *
369
- * In general, if we want to batch insert items, we first to update their low nullifier to point to them,
370
- * then batch insert all of the values as at once in the final step.
323
+ * In general, if we want to batch insert items, we first need to update their low nullifier to point to them,
324
+ * then batch insert all of the values at once in the final step.
371
325
  * To update a low nullifier, we provide an insertion proof that the low nullifier currently exists to the
372
326
  * circuit, then update the low nullifier.
373
327
  * Updating this low nullifier will in turn change the root of the tree. Therefore future low nullifier insertion proofs
@@ -422,7 +376,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
422
376
  * nextVal 2 10 15 0 3 5 - -
423
377
  *
424
378
  * Inserting 20: (happy path)
425
- * 1. Find the low nullifier (15) - provide inculsion proof
379
+ * 1. Find the low nullifier (15) - provide inclusion proof
426
380
  * 2. Update its pointers
427
381
  * 3. Insert 20 into the pending subtree
428
382
  *
@@ -454,7 +408,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
454
408
  *
455
409
  * TODO: this implementation will change once the zero value is changed from h(0,0,0). Changes incoming over the next sprint
456
410
  * @param leaves - Values to insert into the tree.
457
- * @param treeHeight - Height of the tree.
458
411
  * @param subtreeHeight - Height of the subtree.
459
412
  * @returns The data for the leaves to be updated when inserting the new ones.
460
413
  */
@@ -464,7 +417,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
464
417
  SubtreeSiblingPathHeight extends number,
465
418
  >(
466
419
  leaves: Buffer[],
467
- treeHeight: TreeHeight,
468
420
  subtreeHeight: SubtreeHeight,
469
421
  ): Promise<
470
422
  | [LowLeafWitnessData<TreeHeight>[], SiblingPath<SubtreeSiblingPathHeight>]
@@ -473,7 +425,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
473
425
  // Keep track of touched low leaves
474
426
  const touched = new Map<number, bigint[]>();
475
427
 
476
- const emptyLowLeafWitness = getEmptyLowLeafWitness(treeHeight);
428
+ const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight);
477
429
  // Accumulators
478
430
  const lowLeavesWitnesses: LowLeafWitnessData<TreeHeight>[] = [];
479
431
  const pendingInsertionSubtree: LeafData[] = [];
@@ -494,7 +446,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
494
446
 
495
447
  const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true);
496
448
 
497
- // If a touched node has a value that is less greater than the current value
449
+ // If a touched node has a value that is less than the current value
498
450
  const prevNodes = touched.get(indexOfPrevious.index);
499
451
  if (prevNodes && prevNodes.some(v => v < newValue)) {
500
452
  // check the pending low nullifiers for a low nullifier that works
@@ -561,7 +513,9 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
561
513
  lowLeaf.nextValue = newValue;
562
514
  lowLeaf.nextIndex = startInsertionIndex + BigInt(i);
563
515
 
564
- await this.updateLeaf(lowLeaf, BigInt(indexOfPrevious.index));
516
+ const lowLeafIndex = indexOfPrevious.index;
517
+ this.cachedLeaves[lowLeafIndex] = lowLeaf;
518
+ await this.updateLeaf(lowLeaf, BigInt(lowLeafIndex));
565
519
  }
566
520
  }
567
521
 
@@ -571,10 +525,9 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
571
525
  );
572
526
 
573
527
  // Perform batch insertion of new pending values
574
- for (let i = 0; i < pendingInsertionSubtree.length; i++) {
575
- await this.updateLeaf(pendingInsertionSubtree[i], startInsertionIndex + BigInt(i));
576
- }
577
-
528
+ // Note: In this case we set `hash0Leaf` param to false because batch insertion algorithm use forced null leaf
529
+ // inclusion. See {@link encodeLeaf} for a more through param explanation.
530
+ await this.encodeAndAppendLeaves(pendingInsertionSubtree, false);
578
531
  return [lowLeavesWitnesses, newSubtreeSiblingPath];
579
532
  }
580
533
 
@@ -588,4 +541,41 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
588
541
  // Drop the first subtreeHeight items since we only care about the path to the subtree root
589
542
  return fullSiblingPath.getSubtreeSiblingPath(subtreeHeight);
590
543
  }
544
+
545
+ /**
546
+ * Encodes leaves and appends them to a tree.
547
+ * @param leaves - Leaves to encode.
548
+ * @param hash0Leaf - Indicates whether 0 value leaf should be hashed. See {@link encodeLeaf}.
549
+ * @returns Empty promise
550
+ */
551
+ private async encodeAndAppendLeaves(leaves: LeafData[], hash0Leaf: boolean): Promise<void> {
552
+ const startInsertionIndex = Number(this.getNumLeaves(true));
553
+
554
+ const serialisedLeaves = leaves.map((leaf, i) => {
555
+ this.cachedLeaves[startInsertionIndex + i] = leaf;
556
+ return this.encodeLeaf(leaf, hash0Leaf);
557
+ });
558
+
559
+ await super.appendLeaves(serialisedLeaves);
560
+ }
561
+
562
+ /**
563
+ * Encode a leaf into a buffer.
564
+ * @param leaf - Leaf to encode.
565
+ * @param hash0Leaf - Indicates whether 0 value leaf should be hashed. Not hashing 0 value can represent a forced
566
+ * null leaf insertion. Detecting this case by checking for 0 value is safe as in the case of
567
+ * nullifier it is improbable that a valid nullifier would be 0.
568
+ * @returns Leaf encoded in a buffer.
569
+ */
570
+ private encodeLeaf(leaf: LeafData, hash0Leaf: boolean): Buffer {
571
+ let encodedLeaf;
572
+ if (!hash0Leaf && leaf.value == 0n) {
573
+ encodedLeaf = toBufferBE(0n, 32);
574
+ } else {
575
+ encodedLeaf = this.hasher.compressInputs(
576
+ [leaf.value, leaf.nextIndex, leaf.nextValue].map(val => toBufferBE(val, 32)),
577
+ );
578
+ }
579
+ return encodedLeaf;
580
+ }
591
581
  }
@@ -1,21 +1,21 @@
1
1
  import { default as levelup } from 'levelup';
2
- import { Hasher, INITIAL_LEAF, MerkleTree, Pedersen, SiblingPath } from '../index.js';
3
- import { StandardIndexedTree } from './standard_indexed_tree.js';
4
- import { treeTestSuite } from '../test/test_suite.js';
2
+ import { Hasher, INITIAL_LEAF, MerkleTree, Pedersen, SiblingPath } from '../../index.js';
3
+ import { treeTestSuite } from '../../test/test_suite.js';
5
4
 
6
- import { createMemDown } from '../test/utils/create_mem_down.js';
7
- import { newTree } from '../new_tree.js';
8
- import { loadTree } from '../load_tree.js';
5
+ import { createMemDown } from '../../test/utils/create_mem_down.js';
6
+ import { newTree } from '../../new_tree.js';
7
+ import { loadTree } from '../../load_tree.js';
9
8
  import { toBufferBE } from '@aztec/foundation/bigint-buffer';
10
9
  import { IWasmModule } from '@aztec/foundation/wasm';
11
10
  import { CircuitsWasm } from '@aztec/circuits.js';
11
+ import { StandardIndexedTreeWithAppend } from './standard_indexed_tree_with_append.js';
12
12
 
13
- const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => {
14
- return await newTree(StandardIndexedTree, levelUp, hasher, name, depth);
13
+ const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => {
14
+ return await newTree(StandardIndexedTreeWithAppend, levelUp, hasher, name, depth, prefilledSize);
15
15
  };
16
16
 
17
17
  const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => {
18
- return await loadTree(StandardIndexedTree, levelUp, hasher, name);
18
+ return await loadTree(StandardIndexedTreeWithAppend, levelUp, hasher, name);
19
19
  };
20
20
 
21
21
  const createIndexedTreeLeaf = (value: number, nextIndex: number, nextValue: number) => {
@@ -447,4 +447,30 @@ describe('StandardIndexedTreeSpecific', () => {
447
447
  expect(await tree.getSiblingPath(BigInt(i), false)).toEqual(await tree.getSiblingPath(BigInt(i), true));
448
448
  }
449
449
  });
450
+
451
+ // For varying orders of insertions assert the local batch insertion generator creates the correct proofs
452
+ it.each([
453
+ // These are arbitrary but it needs to be higher than the constant `INITIAL_NULLIFIER_TREE_SIZE` and `KERNEL_NEW_NULLIFIERS_LENGTH * 2`
454
+ [[1003, 1002, 1001, 1000, 0, 0, 0, 0]],
455
+ [[1003, 1004, 1005, 1006, 0, 0, 0, 0]],
456
+ [[1234, 1098, 0, 0, 99999, 1096, 1054, 0]],
457
+ [[1970, 1980, 1040, 0, 99999, 1880, 100001, 9000000]],
458
+ ] as const)('performs nullifier tree batch insertion correctly', async nullifiers => {
459
+ const leaves = nullifiers.map(i => toBufferBE(BigInt(i), 32));
460
+
461
+ const TREE_HEIGHT = 16; // originally from NULLIFIER_TREE_HEIGHT
462
+ const INITIAL_TREE_SIZE = 8; // originally from INITIAL_NULLIFIER_TREE_SIZE
463
+ const SUBTREE_HEIGHT = 5; // originally from BaseRollupInputs.NULLIFIER_SUBTREE_HEIGHT
464
+
465
+ // Create a depth-3 indexed merkle tree
466
+ const appendTree = await createDb(levelup(createMemDown()), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE);
467
+ const insertTree = await createDb(levelup(createMemDown()), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE);
468
+
469
+ await appendTree.appendLeaves(leaves);
470
+ await insertTree.batchInsert(leaves, SUBTREE_HEIGHT);
471
+
472
+ const expectedRoot = appendTree.getRoot(true);
473
+ const actualRoot = insertTree.getRoot(true);
474
+ expect(actualRoot).toEqual(expectedRoot);
475
+ });
450
476
  });
@@ -0,0 +1,63 @@
1
+ import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
2
+ import { LeafData, StandardIndexedTree } from '../../index.js';
3
+
4
+ /**
5
+ * A testing utility which is here to store the original implementation of StandardIndexedTree.appendLeaves method
6
+ * that was replaced by the more efficient batchInsert method. We keep the original implementation around as it useful
7
+ * for testing that the more complex batchInsert method works correctly.
8
+ */
9
+ export class StandardIndexedTreeWithAppend extends StandardIndexedTree {
10
+ /**
11
+ * Appends the given leaves to the tree.
12
+ * @param leaves - The leaves to append.
13
+ * @returns Empty promise.
14
+ * @remarks This method is inefficient and is here mostly for testing. Use batchInsert instead.
15
+ */
16
+ public async appendLeaves(leaves: Buffer[]): Promise<void> {
17
+ for (const leaf of leaves) {
18
+ await this.appendLeaf(leaf);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Appends the given leaf to the tree.
24
+ * @param leaf - The leaf to append.
25
+ * @returns Empty promise.
26
+ */
27
+ private async appendLeaf(leaf: Buffer): Promise<void> {
28
+ const newValue = toBigIntBE(leaf);
29
+
30
+ // Special case when appending zero
31
+ if (newValue === 0n) {
32
+ const newSize = (this.cachedSize ?? this.size) + 1n;
33
+ if (newSize - 1n > this.maxIndex) {
34
+ throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
35
+ }
36
+ this.cachedSize = newSize;
37
+ return;
38
+ }
39
+
40
+ const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true);
41
+ const previousLeafCopy = this.getLatestLeafDataCopy(indexOfPrevious.index, true);
42
+
43
+ if (previousLeafCopy === undefined) {
44
+ throw new Error(`Previous leaf not found!`);
45
+ }
46
+ const newLeaf = {
47
+ value: newValue,
48
+ nextIndex: previousLeafCopy.nextIndex,
49
+ nextValue: previousLeafCopy.nextValue,
50
+ } as LeafData;
51
+ if (indexOfPrevious.alreadyPresent) {
52
+ return;
53
+ }
54
+ // insert a new leaf at the highest index and update the values of our previous leaf copy
55
+ const currentSize = this.getNumLeaves(true);
56
+ previousLeafCopy.nextIndex = BigInt(currentSize);
57
+ previousLeafCopy.nextValue = newLeaf.value;
58
+ this.cachedLeaves[Number(currentSize)] = newLeaf;
59
+ this.cachedLeaves[Number(indexOfPrevious.index)] = previousLeafCopy;
60
+ await this.updateLeaf(previousLeafCopy, BigInt(indexOfPrevious.index));
61
+ await this.updateLeaf(newLeaf, this.getNumLeaves(true));
62
+ }
63
+ }