@aztec/kv-store 0.0.0-test.1 → 0.0.1-commit.017a351

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 (221) hide show
  1. package/README.md +8 -1
  2. package/dest/bench/shared_map_bench.d.ts +19 -0
  3. package/dest/bench/shared_map_bench.d.ts.map +1 -0
  4. package/dest/bench/shared_map_bench.js +91 -0
  5. package/dest/indexeddb/array.d.ts +3 -2
  6. package/dest/indexeddb/array.d.ts.map +1 -1
  7. package/dest/indexeddb/array.js +24 -7
  8. package/dest/indexeddb/index.d.ts +3 -3
  9. package/dest/indexeddb/index.d.ts.map +1 -1
  10. package/dest/indexeddb/index.js +4 -7
  11. package/dest/indexeddb/map.d.ts +18 -6
  12. package/dest/indexeddb/map.d.ts.map +1 -1
  13. package/dest/indexeddb/map.js +51 -63
  14. package/dest/indexeddb/multi_map.d.ts +14 -0
  15. package/dest/indexeddb/multi_map.d.ts.map +1 -0
  16. package/dest/indexeddb/multi_map.js +107 -0
  17. package/dest/indexeddb/set.d.ts +1 -1
  18. package/dest/indexeddb/set.d.ts.map +1 -1
  19. package/dest/indexeddb/singleton.d.ts +3 -2
  20. package/dest/indexeddb/singleton.d.ts.map +1 -1
  21. package/dest/indexeddb/singleton.js +6 -2
  22. package/dest/indexeddb/store.d.ts +15 -16
  23. package/dest/indexeddb/store.d.ts.map +1 -1
  24. package/dest/indexeddb/store.js +51 -46
  25. package/dest/interfaces/array.d.ts +5 -4
  26. package/dest/interfaces/array.d.ts.map +1 -1
  27. package/dest/interfaces/array.js +1 -3
  28. package/dest/interfaces/array_test_suite.d.ts +1 -1
  29. package/dest/interfaces/array_test_suite.d.ts.map +1 -1
  30. package/dest/interfaces/array_test_suite.js +33 -34
  31. package/dest/interfaces/common.d.ts +11 -9
  32. package/dest/interfaces/common.d.ts.map +1 -1
  33. package/dest/interfaces/common.js +8 -3
  34. package/dest/interfaces/counter.d.ts +1 -1
  35. package/dest/interfaces/index.d.ts +4 -2
  36. package/dest/interfaces/index.d.ts.map +1 -1
  37. package/dest/interfaces/index.js +2 -0
  38. package/dest/interfaces/map.d.ts +21 -49
  39. package/dest/interfaces/map.d.ts.map +1 -1
  40. package/dest/interfaces/map.js +1 -1
  41. package/dest/interfaces/map_test_suite.d.ts +1 -1
  42. package/dest/interfaces/map_test_suite.d.ts.map +1 -1
  43. package/dest/interfaces/map_test_suite.js +193 -83
  44. package/dest/interfaces/multi_map.d.ts +41 -0
  45. package/dest/interfaces/multi_map.d.ts.map +1 -0
  46. package/dest/interfaces/multi_map.js +3 -0
  47. package/dest/interfaces/multi_map_test_suite.d.ts +3 -0
  48. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -0
  49. package/dest/interfaces/multi_map_test_suite.js +363 -0
  50. package/dest/interfaces/set.d.ts +1 -1
  51. package/dest/interfaces/set_test_suite.d.ts +1 -1
  52. package/dest/interfaces/set_test_suite.d.ts.map +1 -1
  53. package/dest/interfaces/set_test_suite.js +13 -14
  54. package/dest/interfaces/singleton.d.ts +1 -1
  55. package/dest/interfaces/singleton_test_suite.d.ts +1 -1
  56. package/dest/interfaces/singleton_test_suite.d.ts.map +1 -1
  57. package/dest/interfaces/singleton_test_suite.js +6 -7
  58. package/dest/interfaces/store.d.ts +18 -43
  59. package/dest/interfaces/store.d.ts.map +1 -1
  60. package/dest/interfaces/utils.d.ts +3 -1
  61. package/dest/interfaces/utils.d.ts.map +1 -1
  62. package/dest/interfaces/utils.js +3 -1
  63. package/dest/lmdb/array.d.ts +3 -2
  64. package/dest/lmdb/array.d.ts.map +1 -1
  65. package/dest/lmdb/array.js +4 -2
  66. package/dest/lmdb/counter.d.ts +1 -1
  67. package/dest/lmdb/counter.d.ts.map +1 -1
  68. package/dest/lmdb/index.d.ts +3 -3
  69. package/dest/lmdb/index.d.ts.map +1 -1
  70. package/dest/lmdb/index.js +6 -6
  71. package/dest/lmdb/map.d.ts +11 -23
  72. package/dest/lmdb/map.d.ts.map +1 -1
  73. package/dest/lmdb/map.js +15 -81
  74. package/dest/lmdb/multi_map.d.ts +13 -0
  75. package/dest/lmdb/multi_map.d.ts.map +1 -0
  76. package/dest/lmdb/multi_map.js +44 -0
  77. package/dest/lmdb/set.d.ts +1 -1
  78. package/dest/lmdb/set.d.ts.map +1 -1
  79. package/dest/lmdb/singleton.d.ts +1 -1
  80. package/dest/lmdb/singleton.d.ts.map +1 -1
  81. package/dest/lmdb/store.d.ts +11 -27
  82. package/dest/lmdb/store.d.ts.map +1 -1
  83. package/dest/lmdb/store.js +24 -40
  84. package/dest/lmdb-v2/array.d.ts +4 -3
  85. package/dest/lmdb-v2/array.d.ts.map +1 -1
  86. package/dest/lmdb-v2/array.js +4 -2
  87. package/dest/lmdb-v2/factory.d.ts +32 -10
  88. package/dest/lmdb-v2/factory.d.ts.map +1 -1
  89. package/dest/lmdb-v2/factory.js +83 -18
  90. package/dest/lmdb-v2/index.d.ts +1 -1
  91. package/dest/lmdb-v2/map.d.ts +12 -45
  92. package/dest/lmdb-v2/map.d.ts.map +1 -1
  93. package/dest/lmdb-v2/map.js +17 -104
  94. package/dest/lmdb-v2/message.d.ts +24 -5
  95. package/dest/lmdb-v2/message.d.ts.map +1 -1
  96. package/dest/lmdb-v2/message.js +6 -4
  97. package/dest/lmdb-v2/multi_map.d.ts +52 -0
  98. package/dest/lmdb-v2/multi_map.d.ts.map +1 -0
  99. package/dest/lmdb-v2/multi_map.js +123 -0
  100. package/dest/lmdb-v2/read_transaction.d.ts +3 -1
  101. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -1
  102. package/dest/lmdb-v2/read_transaction.js +47 -11
  103. package/dest/lmdb-v2/set.d.ts +15 -0
  104. package/dest/lmdb-v2/set.d.ts.map +1 -0
  105. package/dest/lmdb-v2/set.js +23 -0
  106. package/dest/lmdb-v2/singleton.d.ts +2 -2
  107. package/dest/lmdb-v2/singleton.d.ts.map +1 -1
  108. package/dest/lmdb-v2/singleton.js +1 -1
  109. package/dest/lmdb-v2/store.d.ts +13 -13
  110. package/dest/lmdb-v2/store.d.ts.map +1 -1
  111. package/dest/lmdb-v2/store.js +22 -32
  112. package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
  113. package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
  114. package/dest/lmdb-v2/tx-helpers.js +21 -0
  115. package/dest/lmdb-v2/utils.d.ts +3 -14
  116. package/dest/lmdb-v2/utils.d.ts.map +1 -1
  117. package/dest/lmdb-v2/utils.js +0 -94
  118. package/dest/lmdb-v2/write_transaction.d.ts +3 -5
  119. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
  120. package/dest/lmdb-v2/write_transaction.js +9 -5
  121. package/dest/sqlite-opfs/array.d.ts +21 -0
  122. package/dest/sqlite-opfs/array.d.ts.map +1 -0
  123. package/dest/sqlite-opfs/array.js +128 -0
  124. package/dest/sqlite-opfs/errors.d.ts +27 -0
  125. package/dest/sqlite-opfs/errors.d.ts.map +1 -0
  126. package/dest/sqlite-opfs/errors.js +34 -0
  127. package/dest/sqlite-opfs/index.d.ts +16 -0
  128. package/dest/sqlite-opfs/index.d.ts.map +1 -0
  129. package/dest/sqlite-opfs/index.js +22 -0
  130. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts +32 -0
  131. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts.map +1 -0
  132. package/dest/sqlite-opfs/internal/ordered-binary-browser.js +448 -0
  133. package/dest/sqlite-opfs/map.d.ts +35 -0
  134. package/dest/sqlite-opfs/map.d.ts.map +1 -0
  135. package/dest/sqlite-opfs/map.js +163 -0
  136. package/dest/sqlite-opfs/messages.d.ts +66 -0
  137. package/dest/sqlite-opfs/messages.d.ts.map +1 -0
  138. package/dest/sqlite-opfs/messages.js +5 -0
  139. package/dest/sqlite-opfs/multi_map.d.ts +16 -0
  140. package/dest/sqlite-opfs/multi_map.d.ts.map +1 -0
  141. package/dest/sqlite-opfs/multi_map.js +67 -0
  142. package/dest/sqlite-opfs/set.d.ts +13 -0
  143. package/dest/sqlite-opfs/set.d.ts.map +1 -0
  144. package/dest/sqlite-opfs/set.js +19 -0
  145. package/dest/sqlite-opfs/singleton.d.ts +13 -0
  146. package/dest/sqlite-opfs/singleton.d.ts.map +1 -0
  147. package/dest/sqlite-opfs/singleton.js +48 -0
  148. package/dest/sqlite-opfs/store.d.ts +79 -0
  149. package/dest/sqlite-opfs/store.d.ts.map +1 -0
  150. package/dest/sqlite-opfs/store.js +281 -0
  151. package/dest/sqlite-opfs/worker.d.ts +2 -0
  152. package/dest/sqlite-opfs/worker.d.ts.map +1 -0
  153. package/dest/sqlite-opfs/worker.js +249 -0
  154. package/dest/stores/index.d.ts +2 -1
  155. package/dest/stores/index.d.ts.map +1 -1
  156. package/dest/stores/index.js +1 -0
  157. package/dest/stores/l2_tips_store.d.ts +25 -9
  158. package/dest/stores/l2_tips_store.d.ts.map +1 -1
  159. package/dest/stores/l2_tips_store.js +64 -46
  160. package/dest/utils.d.ts +9 -6
  161. package/dest/utils.d.ts.map +1 -1
  162. package/dest/utils.js +51 -16
  163. package/package.json +44 -33
  164. package/src/bench/shared_map_bench.ts +111 -0
  165. package/src/indexeddb/array.ts +9 -5
  166. package/src/indexeddb/index.ts +11 -9
  167. package/src/indexeddb/map.ts +51 -56
  168. package/src/indexeddb/multi_map.ts +105 -0
  169. package/src/indexeddb/singleton.ts +5 -2
  170. package/src/indexeddb/store.ts +77 -60
  171. package/src/interfaces/array.ts +5 -3
  172. package/src/interfaces/array_test_suite.ts +33 -35
  173. package/src/interfaces/common.ts +20 -9
  174. package/src/interfaces/index.ts +3 -1
  175. package/src/interfaces/map.ts +19 -53
  176. package/src/interfaces/map_test_suite.ts +113 -58
  177. package/src/interfaces/multi_map.ts +45 -0
  178. package/src/interfaces/multi_map_test_suite.ts +372 -0
  179. package/src/interfaces/set_test_suite.ts +13 -15
  180. package/src/interfaces/singleton_test_suite.ts +6 -8
  181. package/src/interfaces/store.ts +18 -53
  182. package/src/interfaces/utils.ts +2 -0
  183. package/src/lmdb/array.ts +2 -1
  184. package/src/lmdb/index.ts +13 -8
  185. package/src/lmdb/map.ts +23 -94
  186. package/src/lmdb/multi_map.ts +51 -0
  187. package/src/lmdb/store.ts +36 -56
  188. package/src/lmdb-v2/array.ts +8 -3
  189. package/src/lmdb-v2/factory.ts +107 -26
  190. package/src/lmdb-v2/map.ts +30 -127
  191. package/src/lmdb-v2/message.ts +23 -0
  192. package/src/lmdb-v2/multi_map.ts +150 -0
  193. package/src/lmdb-v2/read_transaction.ts +53 -13
  194. package/src/lmdb-v2/set.ts +33 -0
  195. package/src/lmdb-v2/singleton.ts +6 -2
  196. package/src/lmdb-v2/store.ts +28 -44
  197. package/src/lmdb-v2/tx-helpers.ts +29 -0
  198. package/src/lmdb-v2/utils.ts +0 -118
  199. package/src/lmdb-v2/write_transaction.ts +11 -10
  200. package/src/sqlite-opfs/array.ts +124 -0
  201. package/src/sqlite-opfs/errors.ts +44 -0
  202. package/src/sqlite-opfs/index.ts +39 -0
  203. package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -0
  204. package/src/sqlite-opfs/map.ts +163 -0
  205. package/src/sqlite-opfs/messages.ts +39 -0
  206. package/src/sqlite-opfs/multi_map.ts +74 -0
  207. package/src/sqlite-opfs/set.ts +29 -0
  208. package/src/sqlite-opfs/singleton.ts +48 -0
  209. package/src/sqlite-opfs/store.ts +296 -0
  210. package/src/sqlite-opfs/worker.ts +231 -0
  211. package/src/stores/index.ts +2 -0
  212. package/src/stores/l2_tips_store.ts +68 -49
  213. package/src/utils.ts +79 -21
  214. package/dest/config.d.ts +0 -17
  215. package/dest/config.d.ts.map +0 -1
  216. package/dest/config.js +0 -24
  217. package/dest/interfaces/store_test_suite.d.ts +0 -3
  218. package/dest/interfaces/store_test_suite.d.ts.map +0 -1
  219. package/dest/interfaces/store_test_suite.js +0 -37
  220. package/src/config.ts +0 -34
  221. package/src/interfaces/store_test_suite.ts +0 -56
@@ -1,7 +1,16 @@
1
- /** Stores currently synced L2 tips and unfinalized block hashes. */ export class L2TipsStore {
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;
4
- constructor(store, namespace){
10
+ l2BlockNumberToCheckpointNumberStore;
11
+ l2CheckpointStore;
12
+ constructor(store, namespace, initialBlockHash){
13
+ super(initialBlockHash), this.store = store;
5
14
  this.l2TipsStore = store.openMap([
6
15
  namespace,
7
16
  'l2_tips'
@@ -10,56 +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('_'));
13
30
  }
14
- getL2BlockHash(number) {
15
- return this.l2BlockHashesStore.getAsync(number);
31
+ getTip(tag) {
32
+ return this.l2TipsStore.getAsync(tag);
16
33
  }
17
- async getL2Tips() {
18
- return {
19
- latest: await this.getL2Tip('latest'),
20
- finalized: await this.getL2Tip('finalized'),
21
- proven: await this.getL2Tip('proven')
22
- };
34
+ setTip(tag, blockNumber) {
35
+ return this.l2TipsStore.set(tag, blockNumber);
23
36
  }
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
- };
37
+ getStoredBlockHash(blockNumber) {
38
+ return this.l2BlockHashesStore.getAsync(blockNumber);
39
+ }
40
+ setBlockHash(blockNumber, hash) {
41
+ return this.l2BlockHashesStore.set(blockNumber, hash);
42
+ }
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);
61
+ }
62
+ }
63
+ async getCheckpoint(checkpointNumber) {
64
+ const buffer = await this.l2CheckpointStore.getAsync(checkpointNumber);
65
+ if (!buffer) {
66
+ return undefined;
35
67
  }
36
- return {
37
- number: blockNumber,
38
- hash: blockHash
39
- };
68
+ return PublishedCheckpoint.fromBuffer(buffer);
69
+ }
70
+ saveCheckpointData(checkpoint) {
71
+ return this.l2CheckpointStore.set(checkpoint.checkpoint.number, checkpoint.toBuffer());
40
72
  }
41
- async handleBlockStreamEvent(event) {
42
- switch(event.type){
43
- case 'blocks-added':
44
- for (const block of event.blocks){
45
- await this.l2BlockHashesStore.set(block.number, (await block.header.hash()).toString());
46
- }
47
- await this.l2TipsStore.set('latest', event.blocks.at(-1).number);
48
- break;
49
- case 'chain-pruned':
50
- await this.l2TipsStore.set('latest', event.blockNumber);
51
- break;
52
- case 'chain-proven':
53
- await this.l2TipsStore.set('proven', event.blockNumber);
54
- break;
55
- case 'chain-finalized':
56
- await this.l2TipsStore.set('finalized', event.blockNumber);
57
- for await (const key of this.l2BlockHashesStore.keysAsync({
58
- end: event.blockNumber
59
- })){
60
- await this.l2BlockHashesStore.delete(key);
61
- }
62
- break;
73
+ async deleteCheckpointsBefore(checkpointNumber) {
74
+ for await (const key of this.l2CheckpointStore.keysAsync({
75
+ end: checkpointNumber
76
+ })){
77
+ await this.l2CheckpointStore.delete(key);
63
78
  }
64
79
  }
80
+ runInTransaction(fn) {
81
+ return this.store.transactionAsync(fn);
82
+ }
65
83
  }
package/dest/utils.d.ts CHANGED
@@ -1,12 +1,15 @@
1
- import type { EthAddress } from '@aztec/foundation/eth-address';
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
3
  import type { AztecAsyncKVStore, AztecKVStore } from './interfaces/store.js';
4
4
  /**
5
- * Clears the store if the rollup address does not match the one stored in the database.
6
- * This is to prevent data from being accidentally shared between different rollup instances.
5
+ * Clears the store if the schema version or rollup address does not match the one stored in the database.
6
+ * Also clears if migrating from an older store format that didn't track schema version.
7
+ * This is to prevent data from being accidentally mixed up between different rollup instances or schema versions.
7
8
  * @param store - The store to check
9
+ * @param targetSchemaVersion - The current schema version
8
10
  * @param rollupAddress - The ETH address of the rollup contract
9
- * @returns A promise that resolves when the store is cleared, or rejects if the rollup address does not match
11
+ * @param log - Optional logger
12
+ * @returns The store (cleared if necessary)
10
13
  */
11
- export declare function initStoreForRollup<T extends AztecKVStore | AztecAsyncKVStore>(store: T, rollupAddress: EthAddress, log?: Logger): Promise<T>;
12
- //# sourceMappingURL=utils.d.ts.map
14
+ export declare function initStoreForRollupAndSchemaVersion<T extends AztecKVStore | AztecAsyncKVStore>(store: T, schemaVersion: number | undefined, rollupAddress: EthAddress | undefined, log?: Logger): Promise<T>;
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFJcEQsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFHN0U7Ozs7Ozs7OztHQVNHO0FBQ0gsd0JBQXNCLGtDQUFrQyxDQUFDLENBQUMsU0FBUyxZQUFZLEdBQUcsaUJBQWlCLEVBQ2pHLEtBQUssRUFBRSxDQUFDLEVBQ1IsYUFBYSxFQUFFLE1BQU0sR0FBRyxTQUFTLEVBQ2pDLGFBQWEsRUFBRSxVQUFVLEdBQUcsU0FBUyxFQUNyQyxHQUFHLENBQUMsRUFBRSxNQUFNLEdBQ1gsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQXFDWiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG7E;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,iBAAiB,EACjF,KAAK,EAAE,CAAC,EACR,aAAa,EAAE,UAAU,EACzB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,CAAC,CAqBZ"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAIpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG7E;;;;;;;;;GASG;AACH,wBAAsB,kCAAkC,CAAC,CAAC,SAAS,YAAY,GAAG,iBAAiB,EACjG,KAAK,EAAE,CAAC,EACR,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,aAAa,EAAE,UAAU,GAAG,SAAS,EACrC,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,CAAC,CAqCZ"}
package/dest/utils.js CHANGED
@@ -1,24 +1,59 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+ import { DatabaseVersion } from '@aztec/stdlib/database-version/version';
1
3
  import { isSyncStore } from './interfaces/utils.js';
2
4
  /**
3
- * Clears the store if the rollup address does not match the one stored in the database.
4
- * This is to prevent data from being accidentally shared between different rollup instances.
5
+ * Clears the store if the schema version or rollup address does not match the one stored in the database.
6
+ * Also clears if migrating from an older store format that didn't track schema version.
7
+ * This is to prevent data from being accidentally mixed up between different rollup instances or schema versions.
5
8
  * @param store - The store to check
9
+ * @param targetSchemaVersion - The current schema version
6
10
  * @param rollupAddress - The ETH address of the rollup contract
7
- * @returns A promise that resolves when the store is cleared, or rejects if the rollup address does not match
8
- */ export async function initStoreForRollup(store, rollupAddress, log) {
9
- if (!rollupAddress) {
10
- throw new Error('Rollup address is required');
11
- }
12
- const rollupAddressValue = store.openSingleton('rollupAddress');
13
- const rollupAddressString = rollupAddress.toString();
14
- const storedRollupAddressString = isSyncStore(store) ? rollupAddressValue.get() : await rollupAddressValue.getAsync();
15
- if (typeof storedRollupAddressString !== 'undefined' && storedRollupAddressString !== rollupAddressString) {
16
- log?.warn(`Rollup address mismatch. Clearing entire database...`, {
17
- expected: rollupAddressString,
18
- found: storedRollupAddressString
19
- });
11
+ * @param log - Optional logger
12
+ * @returns The store (cleared if necessary)
13
+ */ export async function initStoreForRollupAndSchemaVersion(store, schemaVersion, rollupAddress, log) {
14
+ const targetSchemaVersion = schemaVersion ?? 0;
15
+ const targetRollupAddress = rollupAddress ?? EthAddress.ZERO;
16
+ const targetDatabaseVersion = new DatabaseVersion(targetSchemaVersion, targetRollupAddress);
17
+ // DB version: database schema version + rollup address combined)
18
+ const dbVersion = store.openSingleton('dbVersion');
19
+ const storedDatabaseVersion = isSyncStore(store) ? dbVersion.get() : await dbVersion.getAsync();
20
+ // Check if this is an old format store (has rollupAddress singleton but no dbVersion)
21
+ const oldRollupSingleton = store.openSingleton('rollupAddress');
22
+ const hasOldFormat = isSyncStore(store) ? !storedDatabaseVersion && !!oldRollupSingleton.get() : !storedDatabaseVersion && !!await oldRollupSingleton.getAsync();
23
+ if (hasOldFormat || doesStoreNeedToBeCleared(targetDatabaseVersion, storedDatabaseVersion, targetSchemaVersion, targetRollupAddress, log)) {
24
+ if (hasOldFormat) {
25
+ log?.warn('Detected old store format without dbVersion, clearing database');
26
+ }
20
27
  await store.clear();
21
28
  }
22
- await rollupAddressValue.set(rollupAddressString);
29
+ await dbVersion.set(targetDatabaseVersion.toBuffer().toString('utf-8'));
23
30
  return store;
24
31
  }
32
+ function doesStoreNeedToBeCleared(targetDatabaseVersion, storedDatabaseVersion, targetSchemaVersion, targetRollupAddress, log) {
33
+ if (storedDatabaseVersion) {
34
+ try {
35
+ const storedVersion = DatabaseVersion.fromBuffer(Buffer.from(storedDatabaseVersion, 'utf-8'));
36
+ const cmp = storedVersion.cmp(targetDatabaseVersion);
37
+ if (cmp === undefined) {
38
+ log?.warn('Rollup address changed, clearing database', {
39
+ stored: storedVersion.rollupAddress.toString(),
40
+ current: targetRollupAddress.toString()
41
+ });
42
+ return true;
43
+ }
44
+ if (cmp !== 0) {
45
+ log?.warn('Schema version changed, clearing database', {
46
+ stored: storedVersion.schemaVersion,
47
+ current: targetSchemaVersion
48
+ });
49
+ return true;
50
+ }
51
+ } catch (err) {
52
+ log?.warn('Failed to parse stored version, clearing database', {
53
+ err
54
+ });
55
+ return true;
56
+ }
57
+ }
58
+ return false;
59
+ }
package/package.json CHANGED
@@ -1,59 +1,66 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.0.0-test.1",
3
+ "version": "0.0.1-commit.017a351",
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",
10
- "./stores": "./dest/stores/index.js",
11
- "./config": "./dest/config.js"
11
+ "./sqlite-opfs": "./dest/sqlite-opfs/index.js",
12
+ "./stores": "./dest/stores/index.js"
13
+ },
14
+ "imports": {
15
+ "#msgpackr": {
16
+ "browser": "msgpackr/index-no-eval",
17
+ "default": "msgpackr"
18
+ },
19
+ "#ordered-binary": {
20
+ "browser": "./dest/sqlite-opfs/internal/ordered-binary-browser.js",
21
+ "default": "ordered-binary"
22
+ }
12
23
  },
13
24
  "scripts": {
14
- "build": "yarn clean && tsc -b",
15
- "build:dev": "tsc -b --watch",
25
+ "build": "yarn clean && ../scripts/tsc.sh",
26
+ "build:dev": "../scripts/tsc.sh --watch",
16
27
  "clean": "rm -rf ./dest .tsbuildinfo",
17
- "formatting": "run -T prettier --check ./src && run -T eslint ./src",
18
- "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
19
- "test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json",
20
- "test:browser": "wtr --config ./web-test-runner.config.mjs",
21
- "test": "yarn test:node && yarn test:browser && true"
28
+ "test:node": "vitest run --project node",
29
+ "test:browser": "bash scripts/run-browser-tests.sh",
30
+ "bench:browser": "VITE_BENCH=1 vitest run --project browser src/bench",
31
+ "test": "vitest run",
32
+ "test:jest": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
22
33
  },
23
34
  "inherits": [
24
35
  "../package.common.json",
25
36
  "./package.local.json"
26
37
  ],
27
38
  "dependencies": {
28
- "@aztec/ethereum": "0.0.0-test.1",
29
- "@aztec/foundation": "0.0.0-test.1",
30
- "@aztec/native": "0.0.0-test.1",
31
- "@aztec/stdlib": "0.0.0-test.1",
39
+ "@aztec/constants": "0.0.1-commit.017a351",
40
+ "@aztec/ethereum": "0.0.1-commit.017a351",
41
+ "@aztec/foundation": "0.0.1-commit.017a351",
42
+ "@aztec/native": "0.0.1-commit.017a351",
43
+ "@aztec/sqlite3mc-wasm": "0.0.1-commit.017a351",
44
+ "@aztec/stdlib": "0.0.1-commit.017a351",
32
45
  "idb": "^8.0.0",
33
46
  "lmdb": "^3.2.0",
34
47
  "msgpackr": "^1.11.2",
48
+ "ohash": "^2.0.11",
35
49
  "ordered-binary": "^1.5.3"
36
50
  },
37
51
  "devDependencies": {
38
- "@jest/globals": "^29.5.0",
39
- "@types/chai": "^5.0.1",
40
- "@types/chai-as-promised": "^8.0.1",
41
- "@types/jest": "^29.5.0",
42
- "@types/mocha": "^10.0.10",
43
- "@types/mocha-each": "^2.0.4",
44
- "@types/node": "^18.7.23",
45
- "@types/sinon": "^17.0.3",
46
- "@web/dev-server-esbuild": "^1.0.3",
47
- "@web/test-runner": "^0.19.0",
48
- "@web/test-runner-playwright": "^0.11.0",
49
- "chai": "^5.1.2",
50
- "chai-as-promised": "^8.0.1",
51
- "jest": "^29.5.0",
52
- "mocha": "^10.8.2",
53
- "mocha-each": "^2.0.1",
54
- "sinon": "^19.0.2",
52
+ "@jest/globals": "^30.0.0",
53
+ "@types/jest": "^30.0.0",
54
+ "@types/node": "^22.15.17",
55
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
56
+ "@vitest/browser-playwright": "^4.0.0",
57
+ "buffer": "^6.0.3",
58
+ "jest": "^30.0.0",
59
+ "playwright": "1.49.0",
55
60
  "ts-node": "^10.9.1",
56
- "typescript": "^5.0.4"
61
+ "typescript": "^5.3.3",
62
+ "util": "^0.12.5",
63
+ "vitest": "^4.0.0"
57
64
  },
58
65
  "files": [
59
66
  "dest",
@@ -61,7 +68,7 @@
61
68
  "!*.test.*"
62
69
  ],
63
70
  "engines": {
64
- "node": ">=18"
71
+ "node": ">=20.10"
65
72
  },
66
73
  "jest": {
67
74
  "extensionsToTreatAsEsm": [
@@ -94,6 +101,10 @@
94
101
  "testTimeout": 120000,
95
102
  "setupFiles": [
96
103
  "../../foundation/src/jest/setup.mjs"
104
+ ],
105
+ "testEnvironment": "../../foundation/src/jest/env.mjs",
106
+ "setupFilesAfterEnv": [
107
+ "../../foundation/src/jest/setupAfterEnv.mjs"
97
108
  ]
98
109
  }
99
110
  }
@@ -0,0 +1,111 @@
1
+ import type { Logger } from '@aztec/foundation/log';
2
+ import { Timer } from '@aztec/foundation/timer';
3
+
4
+ import type { Key } from '../interfaces/common.js';
5
+ import type { AztecAsyncMap } from '../interfaces/map.js';
6
+ import type { AztecAsyncKVStore } from '../interfaces/store.js';
7
+
8
+ /** One benchmark measurement. */
9
+ export type BenchResult = {
10
+ /** Benchmark name (includes the backend prefix for disambiguation). */
11
+ name: string;
12
+ value: number;
13
+ unit: 'ms' | 'us';
14
+ };
15
+
16
+ export type BenchReporter = (results: BenchResult[]) => void | Promise<void>;
17
+
18
+ /**
19
+ * Runs the standard Map benchmark suite against any `AztecAsyncKVStore` backend,
20
+ * populates `results`, and calls `reporter` in `afterAll`.
21
+ *
22
+ * Kept free of Node-only deps (`fs`, `path`) so the same runner works under
23
+ * vitest-browser for IndexedDB and SQLite-OPFS.
24
+ */
25
+ export function describeAztecMapBench(
26
+ backendPrefix: string,
27
+ getStore: () => Promise<AztecAsyncKVStore>,
28
+ logger: Logger,
29
+ reporter: BenchReporter,
30
+ ) {
31
+ describe(`${backendPrefix} Map benchmarks`, () => {
32
+ let store: AztecAsyncKVStore;
33
+ let map: AztecAsyncMap<Key, string>;
34
+
35
+ const results: BenchResult[] = [];
36
+
37
+ const generateKeyValuePairs = (count: number, offset = 0) => {
38
+ const keys = Array.from({ length: count }, (_, i) => `key-${i + offset}`);
39
+ const values = Array.from({ length: count }, (_, i) => `value-${i + offset}`);
40
+ return keys.map((key, i) => ({ key, value: values[i] }));
41
+ };
42
+
43
+ const record = (name: string, value: number, unit: BenchResult['unit']) => {
44
+ results.push({ name: `${backendPrefix}/Map/${name}`, value, unit });
45
+ };
46
+
47
+ beforeEach(async () => {
48
+ store = await getStore();
49
+ map = store.openMap<Key, string>('test');
50
+ });
51
+
52
+ afterEach(async () => {
53
+ await store.delete();
54
+ });
55
+
56
+ afterAll(async () => {
57
+ const pretty = results.map(r => `${r.name}: ${r.value.toFixed(2)} ${r.unit}`).join('\n');
58
+ logger.info(`\n${pretty}\n`);
59
+ await reporter(results);
60
+ });
61
+
62
+ it('adds individual values', async () => {
63
+ const pairs = generateKeyValuePairs(1000);
64
+ const timer = new Timer();
65
+ for (const pair of pairs) {
66
+ await map.set(pair.key, pair.value);
67
+ }
68
+ record('Individual insertion', timer.ms() / pairs.length, 'ms');
69
+ });
70
+
71
+ it('adds batched values', async () => {
72
+ const batches = Array.from({ length: 100 }, (_, i) => generateKeyValuePairs(1000, i * 1000));
73
+ const timer = new Timer();
74
+ for (const batch of batches) {
75
+ await map.setMany(batch);
76
+ }
77
+ record(`Batch insertion of ${batches[0].length} items`, timer.ms() / batches.length, 'ms');
78
+ });
79
+
80
+ it('reads individual values', async () => {
81
+ const pairs = generateKeyValuePairs(10000);
82
+ await map.setMany(pairs);
83
+ const timer = new Timer();
84
+ for (const pair of pairs) {
85
+ await map.getAsync(pair.key);
86
+ }
87
+ record('Individual read', (timer.ms() * 1000) / pairs.length, 'us');
88
+ });
89
+
90
+ it('reads via a cursor', async () => {
91
+ const pairs = generateKeyValuePairs(10000);
92
+ await map.setMany(pairs);
93
+ const timer = new Timer();
94
+ for await (const _ of map.entriesAsync()) {
95
+ // consume
96
+ }
97
+ record(`Iterator per item read of ${pairs.length} items`, (timer.ms() * 1000) / pairs.length, 'us');
98
+ });
99
+
100
+ it('reads the size of the map', async () => {
101
+ const numIterations = 1000;
102
+ const pairs = generateKeyValuePairs(10000);
103
+ await map.setMany(pairs);
104
+ const timer = new Timer();
105
+ for (let i = 0; i < numIterations; i++) {
106
+ await map.sizeAsync();
107
+ }
108
+ record(`Read size of ${pairs.length} items`, (timer.ms() * 1000) / numIterations, 'us');
109
+ });
110
+ });
111
+ }
@@ -1,12 +1,14 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
+ import { hash } from 'ohash';
2
3
 
3
4
  import type { AztecAsyncArray } from '../interfaces/array.js';
5
+ import type { Value } from '../interfaces/common.js';
4
6
  import type { AztecIDBSchema } from './store.js';
5
7
 
6
8
  /**
7
9
  * A persistent array backed by IndexedDB.
8
10
  */
9
- export class IndexedDBAztecArray<T> implements AztecAsyncArray<T> {
11
+ export class IndexedDBAztecArray<T extends Value> implements AztecAsyncArray<T> {
10
12
  #_db?: IDBPObjectStore<AztecIDBSchema, ['data'], 'data', 'readwrite'>;
11
13
  #rootDB: IDBPDatabase<AztecIDBSchema>;
12
14
  #container: string;
@@ -30,7 +32,7 @@ export class IndexedDBAztecArray<T> implements AztecAsyncArray<T> {
30
32
  return (
31
33
  (await this.db
32
34
  .index('key')
33
- .count(IDBKeyRange.bound([this.#container, this.#name], [this.#container, this.#name]))) ?? 0
35
+ .count(IDBKeyRange.bound([this.#container, [this.#name]], [this.#container, [this.#name]]))) ?? 0
34
36
  );
35
37
  }
36
38
 
@@ -39,8 +41,9 @@ export class IndexedDBAztecArray<T> implements AztecAsyncArray<T> {
39
41
  for (const val of vals) {
40
42
  await this.db.put({
41
43
  value: val,
44
+ hash: hash(val),
42
45
  container: this.#container,
43
- key: this.#name,
46
+ key: [this.#name],
44
47
  keyCount: length + 1,
45
48
  slot: this.#slot(length),
46
49
  });
@@ -86,8 +89,9 @@ export class IndexedDBAztecArray<T> implements AztecAsyncArray<T> {
86
89
 
87
90
  await this.db.put({
88
91
  value: val,
92
+ hash: hash(val),
89
93
  container: this.#container,
90
- key: this.#name,
94
+ key: [this.#name],
91
95
  keyCount: index + 1,
92
96
  slot: this.#slot(index),
93
97
  });
@@ -96,7 +100,7 @@ export class IndexedDBAztecArray<T> implements AztecAsyncArray<T> {
96
100
 
97
101
  async *entriesAsync(): AsyncIterableIterator<[number, T]> {
98
102
  const index = this.db.index('key');
99
- const rangeQuery = IDBKeyRange.bound([this.#container, this.#name], [this.#container, this.#name]);
103
+ const rangeQuery = IDBKeyRange.bound([this.#container, [this.#name]], [this.#container, [this.#name]]);
100
104
  for await (const cursor of index.iterate(rangeQuery)) {
101
105
  yield [cursor.value.keyCount - 1, cursor.value.value] as [number, T];
102
106
  }
@@ -1,12 +1,17 @@
1
1
  import { type Logger, createLogger } from '@aztec/foundation/log';
2
+ import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
2
3
 
3
- import type { DataStoreConfig } from '../config.js';
4
- import { initStoreForRollup } from '../utils.js';
4
+ import { initStoreForRollupAndSchemaVersion } from '../utils.js';
5
5
  import { AztecIndexedDBStore } from './store.js';
6
6
 
7
7
  export { AztecIndexedDBStore } from './store.js';
8
8
 
9
- export async function createStore(name: string, config: DataStoreConfig, log: Logger = createLogger('kv-store')) {
9
+ export async function createStore(
10
+ name: string,
11
+ config: DataStoreConfig,
12
+ schemaVersion: number | undefined = undefined,
13
+ log: Logger = createLogger('kv-store'),
14
+ ) {
10
15
  let { dataDirectory } = config;
11
16
  if (typeof dataDirectory !== 'undefined') {
12
17
  dataDirectory = `${dataDirectory}/${name}`;
@@ -14,14 +19,11 @@ export async function createStore(name: string, config: DataStoreConfig, log: Lo
14
19
 
15
20
  log.info(
16
21
  dataDirectory
17
- ? `Creating ${name} data store at directory ${dataDirectory} with map size ${config.dataStoreMapSizeKB} KB`
18
- : `Creating ${name} ephemeral data store with map size ${config.dataStoreMapSizeKB} KB`,
22
+ ? `Creating ${name} data store at directory ${dataDirectory} with map size ${config.dataStoreMapSizeKb} KB`
23
+ : `Creating ${name} ephemeral data store with map size ${config.dataStoreMapSizeKb} KB`,
19
24
  );
20
25
  const store = await AztecIndexedDBStore.open(createLogger('kv-store:indexeddb'), dataDirectory ?? '', false);
21
- if (config.l1Contracts?.rollupAddress) {
22
- return initStoreForRollup(store, config.l1Contracts.rollupAddress, log);
23
- }
24
- return store;
26
+ return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.rollupAddress, log);
25
27
  }
26
28
 
27
29
  export function openTmpStore(ephemeral: boolean = false): Promise<AztecIndexedDBStore> {