@aztec/kv-store 0.0.1-commit.b655e406 → 0.0.1-commit.c31f2472

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 (109) hide show
  1. package/dest/config.d.ts +1 -1
  2. package/dest/indexeddb/array.d.ts +1 -1
  3. package/dest/indexeddb/array.d.ts.map +1 -1
  4. package/dest/indexeddb/array.js +3 -1
  5. package/dest/indexeddb/index.d.ts +1 -1
  6. package/dest/indexeddb/map.d.ts +1 -1
  7. package/dest/indexeddb/map.d.ts.map +1 -1
  8. package/dest/indexeddb/multi_map.d.ts +2 -1
  9. package/dest/indexeddb/multi_map.d.ts.map +1 -1
  10. package/dest/indexeddb/multi_map.js +14 -0
  11. package/dest/indexeddb/set.d.ts +1 -1
  12. package/dest/indexeddb/set.d.ts.map +1 -1
  13. package/dest/indexeddb/singleton.d.ts +1 -1
  14. package/dest/indexeddb/singleton.d.ts.map +1 -1
  15. package/dest/indexeddb/store.d.ts +1 -1
  16. package/dest/indexeddb/store.d.ts.map +1 -1
  17. package/dest/interfaces/array.d.ts +1 -1
  18. package/dest/interfaces/array_test_suite.d.ts +1 -1
  19. package/dest/interfaces/common.d.ts +1 -1
  20. package/dest/interfaces/counter.d.ts +1 -1
  21. package/dest/interfaces/index.d.ts +1 -1
  22. package/dest/interfaces/map.d.ts +1 -1
  23. package/dest/interfaces/map_test_suite.d.ts +1 -1
  24. package/dest/interfaces/multi_map.d.ts +7 -1
  25. package/dest/interfaces/multi_map.d.ts.map +1 -1
  26. package/dest/interfaces/multi_map_test_suite.d.ts +1 -1
  27. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
  28. package/dest/interfaces/multi_map_test_suite.js +94 -0
  29. package/dest/interfaces/set.d.ts +1 -1
  30. package/dest/interfaces/set_test_suite.d.ts +1 -1
  31. package/dest/interfaces/singleton.d.ts +1 -1
  32. package/dest/interfaces/singleton_test_suite.d.ts +1 -1
  33. package/dest/interfaces/store.d.ts +1 -1
  34. package/dest/interfaces/utils.d.ts +2 -1
  35. package/dest/interfaces/utils.d.ts.map +1 -1
  36. package/dest/interfaces/utils.js +2 -1
  37. package/dest/lmdb/array.d.ts +1 -1
  38. package/dest/lmdb/array.d.ts.map +1 -1
  39. package/dest/lmdb/array.js +4 -2
  40. package/dest/lmdb/counter.d.ts +1 -1
  41. package/dest/lmdb/counter.d.ts.map +1 -1
  42. package/dest/lmdb/index.d.ts +1 -1
  43. package/dest/lmdb/map.d.ts +1 -1
  44. package/dest/lmdb/map.d.ts.map +1 -1
  45. package/dest/lmdb/multi_map.d.ts +2 -1
  46. package/dest/lmdb/multi_map.d.ts.map +1 -1
  47. package/dest/lmdb/multi_map.js +15 -0
  48. package/dest/lmdb/set.d.ts +1 -1
  49. package/dest/lmdb/set.d.ts.map +1 -1
  50. package/dest/lmdb/singleton.d.ts +1 -1
  51. package/dest/lmdb/singleton.d.ts.map +1 -1
  52. package/dest/lmdb/store.d.ts +2 -3
  53. package/dest/lmdb/store.d.ts.map +1 -1
  54. package/dest/lmdb/store.js +1 -1
  55. package/dest/lmdb-v2/array.d.ts +2 -2
  56. package/dest/lmdb-v2/array.d.ts.map +1 -1
  57. package/dest/lmdb-v2/array.js +4 -3
  58. package/dest/lmdb-v2/factory.d.ts +6 -9
  59. package/dest/lmdb-v2/factory.d.ts.map +1 -1
  60. package/dest/lmdb-v2/factory.js +13 -9
  61. package/dest/lmdb-v2/index.d.ts +1 -1
  62. package/dest/lmdb-v2/map.d.ts +2 -2
  63. package/dest/lmdb-v2/map.d.ts.map +1 -1
  64. package/dest/lmdb-v2/map.js +1 -2
  65. package/dest/lmdb-v2/message.d.ts +1 -1
  66. package/dest/lmdb-v2/multi_map.d.ts +3 -2
  67. package/dest/lmdb-v2/multi_map.d.ts.map +1 -1
  68. package/dest/lmdb-v2/multi_map.js +11 -1
  69. package/dest/lmdb-v2/read_transaction.d.ts +1 -1
  70. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -1
  71. package/dest/lmdb-v2/set.d.ts +1 -1
  72. package/dest/lmdb-v2/set.d.ts.map +1 -1
  73. package/dest/lmdb-v2/singleton.d.ts +2 -2
  74. package/dest/lmdb-v2/singleton.d.ts.map +1 -1
  75. package/dest/lmdb-v2/singleton.js +1 -2
  76. package/dest/lmdb-v2/store.d.ts +4 -5
  77. package/dest/lmdb-v2/store.d.ts.map +1 -1
  78. package/dest/lmdb-v2/store.js +3 -25
  79. package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
  80. package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
  81. package/dest/lmdb-v2/tx-helpers.js +21 -0
  82. package/dest/lmdb-v2/utils.d.ts +1 -10
  83. package/dest/lmdb-v2/utils.d.ts.map +1 -1
  84. package/dest/lmdb-v2/utils.js +0 -94
  85. package/dest/lmdb-v2/write_transaction.d.ts +1 -1
  86. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
  87. package/dest/lmdb-v2/write_transaction.js +9 -5
  88. package/dest/stores/index.d.ts +1 -1
  89. package/dest/stores/l2_tips_store.d.ts +24 -9
  90. package/dest/stores/l2_tips_store.d.ts.map +1 -1
  91. package/dest/stores/l2_tips_store.js +61 -52
  92. package/dest/utils.d.ts +1 -1
  93. package/package.json +10 -7
  94. package/src/indexeddb/multi_map.ts +12 -0
  95. package/src/interfaces/multi_map.ts +7 -0
  96. package/src/interfaces/multi_map_test_suite.ts +100 -0
  97. package/src/interfaces/utils.ts +1 -0
  98. package/src/lmdb/multi_map.ts +16 -0
  99. package/src/lmdb/store.ts +1 -1
  100. package/src/lmdb-v2/array.ts +2 -2
  101. package/src/lmdb-v2/factory.ts +14 -10
  102. package/src/lmdb-v2/map.ts +2 -2
  103. package/src/lmdb-v2/multi_map.ts +10 -1
  104. package/src/lmdb-v2/singleton.ts +2 -2
  105. package/src/lmdb-v2/store.ts +5 -31
  106. package/src/lmdb-v2/tx-helpers.ts +29 -0
  107. package/src/lmdb-v2/utils.ts +0 -118
  108. package/src/lmdb-v2/write_transaction.ts +9 -8
  109. package/src/stores/l2_tips_store.ts +65 -56
@@ -1,98 +1,4 @@
1
1
  import { MAXIMUM_KEY, fromBufferKey, toBufferKey } from 'ordered-binary';
2
- export function dedupeSortedArray(arr, cmp) {
3
- for(let i = 0; i < arr.length; i++){
4
- let j = i + 1;
5
- for(; j < arr.length; j++){
6
- const res = cmp(arr[i], arr[j]);
7
- if (res === 0) {
8
- continue;
9
- } else if (res < 0) {
10
- break;
11
- } else {
12
- throw new Error('Array not sorted');
13
- }
14
- }
15
- if (j - i > 1) {
16
- arr.splice(i + 1, j - i - 1);
17
- }
18
- }
19
- }
20
- export function insertIntoSortedArray(arr, item, cmp) {
21
- let left = 0;
22
- let right = arr.length;
23
- while(left < right){
24
- const mid = left + right >> 1;
25
- const comparison = cmp(arr[mid], item);
26
- if (comparison < 0) {
27
- left = mid + 1;
28
- } else {
29
- right = mid;
30
- }
31
- }
32
- arr.splice(left, 0, item);
33
- }
34
- export function findIndexInSortedArray(values, needle, cmp) {
35
- let start = 0;
36
- let end = values.length - 1;
37
- while(start <= end){
38
- const mid = start + ((end - start) / 2 | 0);
39
- const res = cmp(values[mid], needle);
40
- if (res === 0) {
41
- return mid;
42
- } else if (res > 0) {
43
- end = mid - 1;
44
- } else {
45
- start = mid + 1;
46
- }
47
- }
48
- return -1;
49
- }
50
- export function findInSortedArray(values, needle, cmp) {
51
- const idx = findIndexInSortedArray(values, needle, cmp);
52
- return idx > -1 ? values[idx] : undefined;
53
- }
54
- export function removeAnyOf(arr, vals, cmp) {
55
- let writeIdx = 0;
56
- let readIdx = 0;
57
- let valIdx = 0;
58
- while(readIdx < arr.length && valIdx < vals.length){
59
- const comparison = cmp(arr[readIdx], vals[valIdx]);
60
- if (comparison < 0) {
61
- arr[writeIdx++] = arr[readIdx++];
62
- } else if (comparison > 0) {
63
- valIdx++;
64
- } else {
65
- readIdx++;
66
- }
67
- }
68
- while(readIdx < arr.length){
69
- arr[writeIdx++] = arr[readIdx++];
70
- }
71
- arr.length = writeIdx;
72
- }
73
- export function removeFromSortedArray(arr, val, cmp) {
74
- const idx = findIndexInSortedArray(arr, val, cmp);
75
- if (idx > -1) {
76
- arr.splice(idx, 1);
77
- }
78
- }
79
- export function merge(arr, toInsert, cmp) {
80
- const result = new Array(arr.length + toInsert.length);
81
- let i = 0, j = 0, k = 0;
82
- while(i < arr.length && j < toInsert.length){
83
- result[k++] = cmp(arr[i], toInsert[j]) <= 0 ? arr[i++] : toInsert[j++];
84
- }
85
- while(i < arr.length){
86
- result[k++] = arr[i++];
87
- }
88
- while(j < toInsert.length){
89
- result[k++] = toInsert[j++];
90
- }
91
- for(i = 0; i < result.length; i++){
92
- arr[i] = result[i];
93
- }
94
- arr.length = result.length;
95
- }
96
2
  export function keyCmp(a, b) {
97
3
  return Buffer.compare(a[0], b[0]);
98
4
  }
@@ -14,4 +14,4 @@ export declare class WriteTransaction extends ReadTransaction {
14
14
  iterateIndex(startKey: Uint8Array, endKey?: Uint8Array, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array[]]>;
15
15
  commit(): Promise<void>;
16
16
  }
17
- //# sourceMappingURL=write_transaction.d.ts.map
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGVfdHJhbnNhY3Rpb24uZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sbWRiLXYyL3dyaXRlX3RyYW5zYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVVBLE9BQU8sRUFBRSxLQUFLLEtBQUssRUFBNkIsTUFBTSxjQUFjLENBQUM7QUFDckUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBR3hELHFCQUFhLGdCQUFpQixTQUFRLGVBQWU7O0lBRW5ELFNBQWdCLFNBQVMsRUFBRSxLQUFLLENBRzlCO0lBQ0YsU0FBZ0IsVUFBVSxFQUFFLEtBQUssQ0FHL0I7SUFFRixHQUFHLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FnQnJEO0lBRUQsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQVlyQztJQUVxQixHQUFHLENBQUMsR0FBRyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQyxDQWF0RTtJQUVELFFBQVEsQ0FBQyxHQUFHLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0EyQnhEO0lBRUQsV0FBVyxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQXFDM0Q7SUFFcUIsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDLENBcUJqRTtJQUVzQixPQUFPLENBQzVCLFFBQVEsRUFBRSxVQUFVLEVBQ3BCLE1BQU0sQ0FBQyxFQUFFLFVBQVUsRUFDbkIsT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUNqQixLQUFLLENBQUMsRUFBRSxNQUFNLEdBQ2IsYUFBYSxDQUFDLENBQUMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBV3pDO0lBRXNCLFlBQVksQ0FDakMsUUFBUSxFQUFFLFVBQVUsRUFDcEIsTUFBTSxDQUFDLEVBQUUsVUFBVSxFQUNuQixPQUFPLENBQUMsRUFBRSxPQUFPLEVBQ2pCLEtBQUssQ0FBQyxFQUFFLE1BQU0sR0FDYixhQUFhLENBQUMsQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQW9CM0M7SUFrR1ksTUFBTSxrQkFTbEI7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAchB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAevE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzD,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB3C,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAanB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAqH/B,MAAM;CAUpB"}
1
+ {"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBrD;IAED,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrC;IAEqB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAatE;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BxD;IAED,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC3D;IAEqB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAqBjE;IAEsB,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAWzC;IAEsB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAoB3C;IAkGY,MAAM,kBASlB;CACF"}
@@ -1,6 +1,7 @@
1
+ import { dedupeSortedArray, findInSortedArray, findIndexInSortedArray, insertIntoSortedArray, merge, removeAnyOf, removeFromSortedArray } from '@aztec/foundation/array';
1
2
  import { Database, LMDBMessageType } from './message.js';
2
3
  import { ReadTransaction } from './read_transaction.js';
3
- import { dedupeSortedArray, findInSortedArray, findIndexInSortedArray, insertIntoSortedArray, keyCmp, merge, removeAnyOf, removeFromSortedArray, singleKeyCmp } from './utils.js';
4
+ import { keyCmp, singleKeyCmp } from './utils.js';
4
5
  export class WriteTransaction extends ReadTransaction {
5
6
  // exposed for tests
6
7
  dataBatch = {
@@ -35,10 +36,10 @@ export class WriteTransaction extends ReadTransaction {
35
36
  remove(key) {
36
37
  const removeEntryIndex = findIndexInSortedArray(this.dataBatch.removeEntries, key, singleKeyCmp);
37
38
  if (removeEntryIndex === -1) {
38
- this.dataBatch.removeEntries.push([
39
+ insertIntoSortedArray(this.dataBatch.removeEntries, [
39
40
  key,
40
41
  null
41
- ]);
42
+ ], keyCmp);
42
43
  }
43
44
  const addEntryIndex = findIndexInSortedArray(this.dataBatch.addEntries, key, singleKeyCmp);
44
45
  if (addEntryIndex > -1) {
@@ -155,10 +156,13 @@ export class WriteTransaction extends ReadTransaction {
155
156
  }
156
157
  async *#iterate(iterator, batch, startKey, endKey, reverse = false, limit, merge, map) {
157
158
  this.assertIsOpen();
158
- // make a copy of this in case we're running in reverse
159
+ // Snapshot both add and remove entries at the start of iteration to ensure consistency
159
160
  const uncommittedEntries = [
160
161
  ...batch.addEntries
161
162
  ];
163
+ const removeEntries = [
164
+ ...batch.removeEntries
165
+ ];
162
166
  // used to check we're in the right order when comparing between a key and uncommittedEntries
163
167
  let cmpDirection = -1;
164
168
  if (reverse) {
@@ -198,7 +202,7 @@ export class WriteTransaction extends ReadTransaction {
198
202
  if (!checkLimit()) {
199
203
  break;
200
204
  }
201
- const toRemove = findInSortedArray(batch.removeEntries, key, singleKeyCmp);
205
+ const toRemove = findInSortedArray(removeEntries, key, singleKeyCmp);
202
206
  // at this point we've either exhausted all uncommitted entries,
203
207
  // we reached a key strictly greater/smaller than `key`
204
208
  // or we found the key itself
@@ -1,3 +1,3 @@
1
1
  export * from './l2_tips_store.js';
2
2
  export { L2TipsMemoryStore, type L2TipsStore } from '@aztec/stdlib/block';
3
- //# sourceMappingURL=index.d.ts.map
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxvQkFBb0IsQ0FBQztBQUVuQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsS0FBSyxXQUFXLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQyJ9
@@ -1,14 +1,29 @@
1
- import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider, L2Tips } from '@aztec/stdlib/block';
1
+ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
+ import { type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block';
3
+ import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
2
4
  import type { AztecAsyncKVStore } from '../interfaces/store.js';
3
- /** Stores currently synced L2 tips and unfinalized block hashes. */
4
- export declare class L2TipsKVStore implements L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider {
5
+ /**
6
+ * Persistent implementation of L2 tips store backed by a KV store.
7
+ * Used by nodes that need to persist chain state across restarts.
8
+ */
9
+ export declare class L2TipsKVStore extends L2TipsStoreBase {
10
+ private store;
5
11
  private readonly l2TipsStore;
6
12
  private readonly l2BlockHashesStore;
13
+ private readonly l2BlockNumberToCheckpointNumberStore;
14
+ private readonly l2CheckpointStore;
7
15
  constructor(store: AztecAsyncKVStore, namespace: string);
8
- getL2BlockHash(number: number): Promise<string | undefined>;
9
- getL2Tips(): Promise<L2Tips>;
10
- private getL2Tip;
11
- handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
12
- private saveTag;
16
+ protected getTip(tag: L2BlockTag): Promise<BlockNumber | undefined>;
17
+ protected setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise<void>;
18
+ protected getStoredBlockHash(blockNumber: BlockNumber): Promise<string | undefined>;
19
+ protected setBlockHash(blockNumber: BlockNumber, hash: string): Promise<void>;
20
+ protected deleteBlockHashesBefore(blockNumber: BlockNumber): Promise<void>;
21
+ protected getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise<CheckpointNumber | undefined>;
22
+ protected setCheckpointNumberForBlock(blockNumber: BlockNumber, checkpointNumber: CheckpointNumber): Promise<void>;
23
+ protected deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise<void>;
24
+ protected getCheckpoint(checkpointNumber: CheckpointNumber): Promise<PublishedCheckpoint | undefined>;
25
+ protected saveCheckpointData(checkpoint: PublishedCheckpoint): Promise<void>;
26
+ protected deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise<void>;
27
+ protected runInTransaction<T>(fn: () => Promise<T>): Promise<T>;
13
28
  }
14
- //# sourceMappingURL=l2_tips_store.d.ts.map
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDJfdGlwc19zdG9yZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3Jlcy9sMl90aXBzX3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNoRixPQUFPLEVBQUUsS0FBSyxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFHL0QsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVoRTs7O0dBR0c7QUFDSCxxQkFBYSxhQUFjLFNBQVEsZUFBZTtJQU85QyxPQUFPLENBQUMsS0FBSztJQU5mLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUF5QztJQUNyRSxPQUFPLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFxQztJQUN4RSxPQUFPLENBQUMsUUFBUSxDQUFDLG9DQUFvQyxDQUErQztJQUNwRyxPQUFPLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUEwQztJQUU1RSxZQUNVLEtBQUssRUFBRSxpQkFBaUIsRUFDaEMsU0FBUyxFQUFFLE1BQU0sRUFTbEI7SUFFRCxTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUMsQ0FFbEU7SUFFRCxTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBRXpFO0lBRUQsU0FBUyxDQUFDLGtCQUFrQixDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsQ0FFbEY7SUFFRCxTQUFTLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBRTVFO0lBRUQsVUFBZ0IsdUJBQXVCLENBQUMsV0FBVyxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBSS9FO0lBRUQsU0FBUyxDQUFDLDJCQUEyQixDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxDQUVyRztJQUVELFNBQVMsQ0FBQywyQkFBMkIsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFakg7SUFFRCxVQUFnQiw2QkFBNkIsQ0FBQyxXQUFXLEVBQUUsV0FBVyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FJckY7SUFFRCxVQUFnQixhQUFhLENBQUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxDQU0xRztJQUVELFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsbUJBQW1CLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUUzRTtJQUVELFVBQWdCLHVCQUF1QixDQUFDLGdCQUFnQixFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FJekY7SUFFRCxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxNQUFNLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBRTlEO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"l2_tips_store.d.ts","sourceRoot":"","sources":["../../src/stores/l2_tips_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,yBAAyB,EACzB,8BAA8B,EAE9B,MAAM,EACP,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,oEAAoE;AACpE,qBAAa,aAAc,YAAW,yBAAyB,EAAE,8BAA8B;IAC7F,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAChE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAgC;gBAEvD,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM;IAKhD,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIrD,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;YAQ3B,QAAQ;IAaT,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;YAyB/D,OAAO;CAMtB"}
1
+ {"version":3,"file":"l2_tips_store.d.ts","sourceRoot":"","sources":["../../src/stores/l2_tips_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAO9C,OAAO,CAAC,KAAK;IANf,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyC;IACrE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqC;IACxE,OAAO,CAAC,QAAQ,CAAC,oCAAoC,CAA+C;IACpG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAE5E,YACU,KAAK,EAAE,iBAAiB,EAChC,SAAS,EAAE,MAAM,EASlB;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAElE;IAED,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;IAED,SAAS,CAAC,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAElF;IAED,SAAS,CAAC,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED,UAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/E;IAED,SAAS,CAAC,2BAA2B,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAErG;IAED,SAAS,CAAC,2BAA2B,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjH;IAED,UAAgB,6BAA6B,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrF;IAED,UAAgB,aAAa,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAM1G;IAED,SAAS,CAAC,kBAAkB,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;IAED,UAAgB,uBAAuB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzF;IAED,SAAS,CAAC,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9D;CACF"}
@@ -1,7 +1,16 @@
1
- /** Stores currently synced L2 tips and unfinalized block hashes. */ export class L2TipsKVStore {
1
+ import { L2TipsStoreBase } from '@aztec/stdlib/block';
2
+ import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
3
+ /**
4
+ * Persistent implementation of L2 tips store backed by a KV store.
5
+ * Used by nodes that need to persist chain state across restarts.
6
+ */ export class L2TipsKVStore extends L2TipsStoreBase {
7
+ store;
2
8
  l2TipsStore;
3
9
  l2BlockHashesStore;
10
+ l2BlockNumberToCheckpointNumberStore;
11
+ l2CheckpointStore;
4
12
  constructor(store, namespace){
13
+ super(), this.store = store;
5
14
  this.l2TipsStore = store.openMap([
6
15
  namespace,
7
16
  'l2_tips'
@@ -10,65 +19,65 @@
10
19
  namespace,
11
20
  'l2_block_hashes'
12
21
  ].join('_'));
22
+ this.l2BlockNumberToCheckpointNumberStore = store.openMap([
23
+ namespace,
24
+ 'l2_block_number_to_checkpoint_number'
25
+ ].join('_'));
26
+ this.l2CheckpointStore = store.openMap([
27
+ namespace,
28
+ 'l2_checkpoint_store'
29
+ ].join('_'));
30
+ }
31
+ getTip(tag) {
32
+ return this.l2TipsStore.getAsync(tag);
33
+ }
34
+ setTip(tag, blockNumber) {
35
+ return this.l2TipsStore.set(tag, blockNumber);
13
36
  }
14
- getL2BlockHash(number) {
15
- return this.l2BlockHashesStore.getAsync(number);
37
+ getStoredBlockHash(blockNumber) {
38
+ return this.l2BlockHashesStore.getAsync(blockNumber);
16
39
  }
17
- async getL2Tips() {
18
- return {
19
- latest: await this.getL2Tip('latest'),
20
- finalized: await this.getL2Tip('finalized'),
21
- proven: await this.getL2Tip('proven')
22
- };
40
+ setBlockHash(blockNumber, hash) {
41
+ return this.l2BlockHashesStore.set(blockNumber, hash);
23
42
  }
24
- async getL2Tip(tag) {
25
- const blockNumber = await this.l2TipsStore.getAsync(tag);
26
- if (blockNumber === undefined || blockNumber === 0) {
27
- return {
28
- number: 0,
29
- hash: undefined
30
- };
43
+ async deleteBlockHashesBefore(blockNumber) {
44
+ for await (const key of this.l2BlockHashesStore.keysAsync({
45
+ end: blockNumber
46
+ })){
47
+ await this.l2BlockHashesStore.delete(key);
31
48
  }
32
- const blockHash = await this.l2BlockHashesStore.getAsync(blockNumber);
33
- if (!blockHash) {
34
- throw new Error(`Block hash not found for block number ${blockNumber}`);
49
+ }
50
+ getCheckpointNumberForBlock(blockNumber) {
51
+ return this.l2BlockNumberToCheckpointNumberStore.getAsync(blockNumber);
52
+ }
53
+ setCheckpointNumberForBlock(blockNumber, checkpointNumber) {
54
+ return this.l2BlockNumberToCheckpointNumberStore.set(blockNumber, checkpointNumber);
55
+ }
56
+ async deleteBlockToCheckpointBefore(blockNumber) {
57
+ for await (const key of this.l2BlockNumberToCheckpointNumberStore.keysAsync({
58
+ end: blockNumber
59
+ })){
60
+ await this.l2BlockNumberToCheckpointNumberStore.delete(key);
35
61
  }
36
- return {
37
- number: blockNumber,
38
- hash: blockHash
39
- };
40
62
  }
41
- async handleBlockStreamEvent(event) {
42
- switch(event.type){
43
- case 'blocks-added':
44
- {
45
- const blocks = event.blocks.map((b)=>b.block);
46
- for (const block of blocks){
47
- await this.l2BlockHashesStore.set(block.number, (await block.hash()).toString());
48
- }
49
- await this.l2TipsStore.set('latest', blocks.at(-1).number);
50
- break;
51
- }
52
- case 'chain-pruned':
53
- await this.saveTag('latest', event.block);
54
- break;
55
- case 'chain-proven':
56
- await this.saveTag('proven', event.block);
57
- break;
58
- case 'chain-finalized':
59
- await this.saveTag('finalized', event.block);
60
- for await (const key of this.l2BlockHashesStore.keysAsync({
61
- end: event.block.number
62
- })){
63
- await this.l2BlockHashesStore.delete(key);
64
- }
65
- break;
63
+ async getCheckpoint(checkpointNumber) {
64
+ const buffer = await this.l2CheckpointStore.getAsync(checkpointNumber);
65
+ if (!buffer) {
66
+ return undefined;
66
67
  }
68
+ return PublishedCheckpoint.fromBuffer(buffer);
67
69
  }
68
- async saveTag(name, block) {
69
- await this.l2TipsStore.set(name, block.number);
70
- if (block.hash) {
71
- await this.l2BlockHashesStore.set(block.number, block.hash);
70
+ saveCheckpointData(checkpoint) {
71
+ return this.l2CheckpointStore.set(checkpoint.checkpoint.number, checkpoint.toBuffer());
72
+ }
73
+ async deleteCheckpointsBefore(checkpointNumber) {
74
+ for await (const key of this.l2CheckpointStore.keysAsync({
75
+ end: checkpointNumber
76
+ })){
77
+ await this.l2CheckpointStore.delete(key);
72
78
  }
73
79
  }
80
+ runInTransaction(fn) {
81
+ return this.store.transactionAsync(fn);
82
+ }
74
83
  }
package/dest/utils.d.ts CHANGED
@@ -9,4 +9,4 @@ import type { AztecAsyncKVStore, AztecKVStore } from './interfaces/store.js';
9
9
  * @returns A promise that resolves when the store is cleared, or rejects if the rollup address does not match
10
10
  */
11
11
  export declare function initStoreForRollup<T extends AztecKVStore | AztecAsyncKVStore>(store: T, rollupAddress: EthAddress, log?: Logger): Promise<T>;
12
- //# sourceMappingURL=utils.d.ts.map
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUc3RTs7Ozs7O0dBTUc7QUFDSCx3QkFBc0Isa0JBQWtCLENBQUMsQ0FBQyxTQUFTLFlBQVksR0FBRyxpQkFBaUIsRUFDakYsS0FBSyxFQUFFLENBQUMsRUFDUixhQUFhLEVBQUUsVUFBVSxFQUN6QixHQUFHLENBQUMsRUFBRSxNQUFNLEdBQ1gsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQXFCWiJ9
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.0.1-commit.b655e406",
3
+ "version": "0.0.1-commit.c31f2472",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/interfaces/index.js",
7
+ "./interfaces": "./dest/interfaces/index.js",
7
8
  "./lmdb": "./dest/lmdb/index.js",
8
9
  "./lmdb-v2": "./dest/lmdb-v2/index.js",
9
10
  "./indexeddb": "./dest/indexeddb/index.js",
@@ -11,8 +12,8 @@
11
12
  "./config": "./dest/config.js"
12
13
  },
13
14
  "scripts": {
14
- "build": "yarn clean && tsc -b",
15
- "build:dev": "tsc -b --watch",
15
+ "build": "yarn clean && ../scripts/tsc.sh",
16
+ "build:dev": "../scripts/tsc.sh --watch",
16
17
  "clean": "rm -rf ./dest .tsbuildinfo",
17
18
  "test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json",
18
19
  "test:browser": "wtr --config ./web-test-runner.config.mjs",
@@ -24,10 +25,11 @@
24
25
  "./package.local.json"
25
26
  ],
26
27
  "dependencies": {
27
- "@aztec/ethereum": "0.0.1-commit.b655e406",
28
- "@aztec/foundation": "0.0.1-commit.b655e406",
29
- "@aztec/native": "0.0.1-commit.b655e406",
30
- "@aztec/stdlib": "0.0.1-commit.b655e406",
28
+ "@aztec/constants": "0.0.1-commit.c31f2472",
29
+ "@aztec/ethereum": "0.0.1-commit.c31f2472",
30
+ "@aztec/foundation": "0.0.1-commit.c31f2472",
31
+ "@aztec/native": "0.0.1-commit.c31f2472",
32
+ "@aztec/stdlib": "0.0.1-commit.c31f2472",
31
33
  "idb": "^8.0.0",
32
34
  "lmdb": "^3.2.0",
33
35
  "msgpackr": "^1.11.2",
@@ -43,6 +45,7 @@
43
45
  "@types/mocha-each": "^2.0.4",
44
46
  "@types/node": "^22.15.17",
45
47
  "@types/sinon": "^17.0.3",
48
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
46
49
  "@web/dev-server-esbuild": "^1.0.3",
47
50
  "@web/test-runner": "^0.19.0",
48
51
  "@web/test-runner-playwright": "^0.11.0",
@@ -61,6 +61,18 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
61
61
  }
62
62
  }
63
63
 
64
+ getValueCountAsync(key: K): Promise<number> {
65
+ // Count entries over the keyCount index range for this key
66
+ const index = this.db.index('keyCount');
67
+ const rangeQuery = IDBKeyRange.bound(
68
+ [this.container, this.normalizeKey(key), 0],
69
+ [this.container, this.normalizeKey(key), Number.MAX_SAFE_INTEGER],
70
+ false,
71
+ false,
72
+ );
73
+ return index.count(rangeQuery);
74
+ }
75
+
64
76
  async deleteValue(key: K, val: V): Promise<void> {
65
77
  // Since we know the value, we can hash it and directly query the "hash" index
66
78
  // to avoid having to iterate over all the values
@@ -29,6 +29,13 @@ export interface AztecAsyncMultiMap<K extends Key, V extends Value> extends Azte
29
29
  */
30
30
  getValuesAsync(key: K): AsyncIterableIterator<V>;
31
31
 
32
+ /**
33
+ * Gets the number of values at the given key.
34
+ * @param key - The key to get the number of values from
35
+ * @returns The number of values at the given key
36
+ */
37
+ getValueCountAsync(key: K): Promise<number>;
38
+
32
39
  /**
33
40
  * Deletes a specific value at the given key.
34
41
  * @param key - The key to delete the value at
@@ -61,6 +61,13 @@ export function describeAztecMultiMap(
61
61
  : await toArray((multiMap as AztecAsyncMultiMap<any, any>).getValuesAsync(key));
62
62
  }
63
63
 
64
+ async function getValueCount(
65
+ key: Key,
66
+ sut: AztecAsyncMultiMap<any, any> | AztecMultiMap<any, any> = multiMap,
67
+ ): Promise<number> {
68
+ return await (sut as AztecAsyncMultiMap<any, any>).getValueCountAsync(key);
69
+ }
70
+
64
71
  it('should be able to set and get values', async () => {
65
72
  await multiMap.set('foo', 'bar');
66
73
  await multiMap.set('baz', 'qux');
@@ -238,5 +245,98 @@ export function describeAztecMultiMap(
238
245
  expect(await keys({ start: 'b', reverse: true })).to.deep.equal(['d', 'c']);
239
246
  expect(await keys({ end: 'b', reverse: true })).to.deep.equal(['b', 'a']);
240
247
  });
248
+
249
+ it('returns 0 for missing key', async () => {
250
+ expect(await getValueCount('missing')).to.equal(0);
251
+ });
252
+
253
+ it('counts a single value', async () => {
254
+ await multiMap.set('foo', 'bar');
255
+ expect(await getValueCount('foo')).to.equal(1);
256
+ });
257
+
258
+ it('counts multiple distinct values for same key', async () => {
259
+ await multiMap.set('foo', 'bar');
260
+ await multiMap.set('foo', 'baz');
261
+ await multiMap.set('foo', 'qux');
262
+ expect(await getValueCount('foo')).to.equal(3);
263
+ });
264
+
265
+ it('does not increase count for duplicate inserts', async () => {
266
+ await multiMap.set('foo', 'bar');
267
+ await multiMap.set('foo', 'bar');
268
+ await multiMap.set('foo', 'baz');
269
+ await multiMap.set('foo', 'baz');
270
+ expect(await getValueCount('foo')).to.equal(2);
271
+ expect(await getValues('foo')).to.have.members(['bar', 'baz']);
272
+ });
273
+
274
+ it('decrements when deleting a single value', async () => {
275
+ await multiMap.set('foo', '1');
276
+ await multiMap.set('foo', '2');
277
+ await multiMap.set('foo', '3');
278
+ expect(await getValueCount('foo')).to.equal(3);
279
+ await multiMap.deleteValue('foo', '2');
280
+ expect(await getValueCount('foo')).to.equal(2);
281
+ expect(await getValues('foo')).to.have.members(['1', '3']);
282
+ });
283
+
284
+ it('does not change count when deleting a non-existent value', async () => {
285
+ await multiMap.set('foo', '1');
286
+ await multiMap.set('foo', '3');
287
+ expect(await getValueCount('foo')).to.equal(2);
288
+ await multiMap.deleteValue('foo', '2');
289
+ expect(await getValueCount('foo')).to.equal(2);
290
+ });
291
+
292
+ it('clears all values when deleting a key', async () => {
293
+ await multiMap.set('foo', 'bar');
294
+ await multiMap.set('foo', 'baz');
295
+ expect(await getValueCount('foo')).to.equal(2);
296
+ await multiMap.delete('foo');
297
+ expect(await getValueCount('foo')).to.equal(0);
298
+ expect(await getValues('foo')).to.deep.equal([]);
299
+ });
300
+
301
+ it('count equals enumerated values length', async () => {
302
+ await multiMap.set('foo', '1');
303
+ await multiMap.set('foo', '2');
304
+ const vals = await getValues('foo');
305
+ expect(await getValueCount('foo')).to.equal(vals.length);
306
+ });
307
+
308
+ it('sum of per-key counts equals total size', async () => {
309
+ await multiMap.set('foo', '1');
310
+ await multiMap.set('foo', '2');
311
+ await multiMap.set('bar', '3');
312
+ await multiMap.set('bar', '4');
313
+ await multiMap.set('baz', '5');
314
+
315
+ const allKeys = await keys();
316
+ const uniqueKeys = Array.from(new Set(allKeys));
317
+ const counts = await Promise.all(uniqueKeys.map(k => getValueCount(k)));
318
+ const sum = counts.reduce((s, n) => s + n, 0);
319
+ expect(sum).to.equal(await size());
320
+ });
321
+
322
+ it('supports sparse slots: delete middle, reinsert new, count remains correct', async () => {
323
+ await multiMap.set('foo', '1');
324
+ await multiMap.set('foo', '2');
325
+ await multiMap.set('foo', '3');
326
+ expect(await getValueCount('foo')).to.equal(3);
327
+ await multiMap.deleteValue('foo', '2');
328
+ expect(await getValueCount('foo')).to.equal(2);
329
+ await multiMap.set('foo', '4');
330
+ expect(await getValueCount('foo')).to.equal(3);
331
+ expect(await getValues('foo')).to.have.members(['1', '3', '4']);
332
+ });
333
+
334
+ it('multiple keys are independent', async () => {
335
+ await multiMap.set('foo', '1');
336
+ await multiMap.set('foo', '2');
337
+ await multiMap.set('bar', '3');
338
+ expect(await getValueCount('foo')).to.equal(2);
339
+ expect(await getValueCount('bar')).to.equal(1);
340
+ });
241
341
  });
242
342
  }
@@ -14,6 +14,7 @@ export const mockLogger = {
14
14
  isLevelEnabled: (_level: string) => true,
15
15
  module: 'kv-store:mock-logger',
16
16
  createChild: () => mockLogger,
17
+ getBindings: () => ({}),
17
18
  };
18
19
  /* eslint-enable no-console */
19
20
 
@@ -29,6 +29,22 @@ export class LmdbAztecMultiMap<K extends Key, V extends Value>
29
29
  }
30
30
  }
31
31
 
32
+ getValueCountAsync(key: K): Promise<number> {
33
+ const transaction = this.db.useReadTransaction();
34
+ try {
35
+ const values = this.db.getValues(this.slot(key), {
36
+ transaction,
37
+ });
38
+ let count = 0;
39
+ for (const _ of values) {
40
+ count++;
41
+ }
42
+ return Promise.resolve(count);
43
+ } finally {
44
+ transaction.done();
45
+ }
46
+ }
47
+
32
48
  async deleteValue(key: K, val: V): Promise<void> {
33
49
  await this.db.remove(this.slot(key), [key, val]);
34
50
  }
package/src/lmdb/store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { randomBytes } from '@aztec/foundation/crypto';
1
+ import { randomBytes } from '@aztec/foundation/crypto/random';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  import { promises as fs, mkdirSync } from 'fs';
@@ -4,8 +4,8 @@ import type { AztecAsyncArray } from '../interfaces/array.js';
4
4
  import type { Value } from '../interfaces/common.js';
5
5
  import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
6
6
  import type { ReadTransaction } from './read_transaction.js';
7
- // eslint-disable-next-line import/no-cycle
8
- import { AztecLMDBStoreV2, execInReadTx, execInWriteTx } from './store.js';
7
+ import type { AztecLMDBStoreV2 } from './store.js';
8
+ import { execInReadTx, execInWriteTx } from './tx-helpers.js';
9
9
  import { deserializeKey, serializeKey } from './utils.js';
10
10
 
11
11
  export class LMDBArray<T extends Value> implements AztecAsyncArray<T> {