@aztec/kv-store 0.0.0-test.1 → 0.0.1-commit.0208eb9

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 (163) hide show
  1. package/dest/config.d.ts +2 -2
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +5 -3
  4. package/dest/indexeddb/array.d.ts +3 -2
  5. package/dest/indexeddb/array.d.ts.map +1 -1
  6. package/dest/indexeddb/array.js +24 -7
  7. package/dest/indexeddb/index.d.ts +2 -2
  8. package/dest/indexeddb/index.d.ts.map +1 -1
  9. package/dest/indexeddb/index.js +4 -7
  10. package/dest/indexeddb/map.d.ts +12 -6
  11. package/dest/indexeddb/map.d.ts.map +1 -1
  12. package/dest/indexeddb/map.js +39 -61
  13. package/dest/indexeddb/multi_map.d.ts +14 -0
  14. package/dest/indexeddb/multi_map.d.ts.map +1 -0
  15. package/dest/indexeddb/multi_map.js +107 -0
  16. package/dest/indexeddb/set.d.ts +1 -1
  17. package/dest/indexeddb/set.d.ts.map +1 -1
  18. package/dest/indexeddb/singleton.d.ts +3 -2
  19. package/dest/indexeddb/singleton.d.ts.map +1 -1
  20. package/dest/indexeddb/singleton.js +6 -2
  21. package/dest/indexeddb/store.d.ts +15 -16
  22. package/dest/indexeddb/store.d.ts.map +1 -1
  23. package/dest/indexeddb/store.js +45 -42
  24. package/dest/interfaces/array.d.ts +5 -4
  25. package/dest/interfaces/array.d.ts.map +1 -1
  26. package/dest/interfaces/array.js +1 -3
  27. package/dest/interfaces/array_test_suite.d.ts +1 -1
  28. package/dest/interfaces/common.d.ts +11 -9
  29. package/dest/interfaces/common.d.ts.map +1 -1
  30. package/dest/interfaces/common.js +8 -3
  31. package/dest/interfaces/counter.d.ts +1 -1
  32. package/dest/interfaces/index.d.ts +4 -2
  33. package/dest/interfaces/index.d.ts.map +1 -1
  34. package/dest/interfaces/index.js +2 -0
  35. package/dest/interfaces/map.d.ts +21 -49
  36. package/dest/interfaces/map.d.ts.map +1 -1
  37. package/dest/interfaces/map.js +1 -1
  38. package/dest/interfaces/map_test_suite.d.ts +1 -1
  39. package/dest/interfaces/map_test_suite.d.ts.map +1 -1
  40. package/dest/interfaces/map_test_suite.js +181 -70
  41. package/dest/interfaces/multi_map.d.ts +41 -0
  42. package/dest/interfaces/multi_map.d.ts.map +1 -0
  43. package/dest/interfaces/multi_map.js +3 -0
  44. package/dest/interfaces/multi_map_test_suite.d.ts +3 -0
  45. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -0
  46. package/dest/interfaces/multi_map_test_suite.js +364 -0
  47. package/dest/interfaces/set.d.ts +1 -1
  48. package/dest/interfaces/set_test_suite.d.ts +1 -1
  49. package/dest/interfaces/singleton.d.ts +1 -1
  50. package/dest/interfaces/singleton_test_suite.d.ts +1 -1
  51. package/dest/interfaces/store.d.ts +18 -43
  52. package/dest/interfaces/store.d.ts.map +1 -1
  53. package/dest/interfaces/utils.d.ts +3 -1
  54. package/dest/interfaces/utils.d.ts.map +1 -1
  55. package/dest/interfaces/utils.js +3 -1
  56. package/dest/lmdb/array.d.ts +3 -2
  57. package/dest/lmdb/array.d.ts.map +1 -1
  58. package/dest/lmdb/array.js +4 -2
  59. package/dest/lmdb/counter.d.ts +1 -1
  60. package/dest/lmdb/counter.d.ts.map +1 -1
  61. package/dest/lmdb/index.d.ts +2 -2
  62. package/dest/lmdb/index.d.ts.map +1 -1
  63. package/dest/lmdb/index.js +5 -5
  64. package/dest/lmdb/map.d.ts +11 -23
  65. package/dest/lmdb/map.d.ts.map +1 -1
  66. package/dest/lmdb/map.js +15 -81
  67. package/dest/lmdb/multi_map.d.ts +13 -0
  68. package/dest/lmdb/multi_map.d.ts.map +1 -0
  69. package/dest/lmdb/multi_map.js +44 -0
  70. package/dest/lmdb/set.d.ts +1 -1
  71. package/dest/lmdb/set.d.ts.map +1 -1
  72. package/dest/lmdb/singleton.d.ts +1 -1
  73. package/dest/lmdb/singleton.d.ts.map +1 -1
  74. package/dest/lmdb/store.d.ts +9 -25
  75. package/dest/lmdb/store.d.ts.map +1 -1
  76. package/dest/lmdb/store.js +12 -32
  77. package/dest/lmdb-v2/array.d.ts +4 -3
  78. package/dest/lmdb-v2/array.d.ts.map +1 -1
  79. package/dest/lmdb-v2/array.js +4 -2
  80. package/dest/lmdb-v2/factory.d.ts +6 -9
  81. package/dest/lmdb-v2/factory.d.ts.map +1 -1
  82. package/dest/lmdb-v2/factory.js +26 -12
  83. package/dest/lmdb-v2/index.d.ts +1 -1
  84. package/dest/lmdb-v2/map.d.ts +12 -45
  85. package/dest/lmdb-v2/map.d.ts.map +1 -1
  86. package/dest/lmdb-v2/map.js +17 -104
  87. package/dest/lmdb-v2/message.d.ts +24 -5
  88. package/dest/lmdb-v2/message.d.ts.map +1 -1
  89. package/dest/lmdb-v2/message.js +6 -4
  90. package/dest/lmdb-v2/multi_map.d.ts +52 -0
  91. package/dest/lmdb-v2/multi_map.d.ts.map +1 -0
  92. package/dest/lmdb-v2/multi_map.js +123 -0
  93. package/dest/lmdb-v2/read_transaction.d.ts +3 -1
  94. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -1
  95. package/dest/lmdb-v2/read_transaction.js +34 -0
  96. package/dest/lmdb-v2/set.d.ts +15 -0
  97. package/dest/lmdb-v2/set.d.ts.map +1 -0
  98. package/dest/lmdb-v2/set.js +23 -0
  99. package/dest/lmdb-v2/singleton.d.ts +2 -2
  100. package/dest/lmdb-v2/singleton.d.ts.map +1 -1
  101. package/dest/lmdb-v2/singleton.js +1 -1
  102. package/dest/lmdb-v2/store.d.ts +13 -13
  103. package/dest/lmdb-v2/store.d.ts.map +1 -1
  104. package/dest/lmdb-v2/store.js +19 -29
  105. package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
  106. package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
  107. package/dest/lmdb-v2/tx-helpers.js +21 -0
  108. package/dest/lmdb-v2/utils.d.ts +3 -14
  109. package/dest/lmdb-v2/utils.d.ts.map +1 -1
  110. package/dest/lmdb-v2/utils.js +0 -94
  111. package/dest/lmdb-v2/write_transaction.d.ts +3 -5
  112. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
  113. package/dest/lmdb-v2/write_transaction.js +9 -5
  114. package/dest/stores/index.d.ts +2 -1
  115. package/dest/stores/index.d.ts.map +1 -1
  116. package/dest/stores/index.js +1 -0
  117. package/dest/stores/l2_tips_store.d.ts +24 -8
  118. package/dest/stores/l2_tips_store.d.ts.map +1 -1
  119. package/dest/stores/l2_tips_store.js +63 -45
  120. package/dest/utils.d.ts +9 -6
  121. package/dest/utils.d.ts.map +1 -1
  122. package/dest/utils.js +51 -16
  123. package/package.json +25 -16
  124. package/src/config.ts +6 -4
  125. package/src/indexeddb/array.ts +9 -5
  126. package/src/indexeddb/index.ts +10 -8
  127. package/src/indexeddb/map.ts +38 -55
  128. package/src/indexeddb/multi_map.ts +105 -0
  129. package/src/indexeddb/singleton.ts +5 -2
  130. package/src/indexeddb/store.ts +71 -56
  131. package/src/interfaces/array.ts +5 -3
  132. package/src/interfaces/common.ts +20 -9
  133. package/src/interfaces/index.ts +3 -1
  134. package/src/interfaces/map.ts +19 -53
  135. package/src/interfaces/map_test_suite.ts +101 -44
  136. package/src/interfaces/multi_map.ts +45 -0
  137. package/src/interfaces/multi_map_test_suite.ts +374 -0
  138. package/src/interfaces/store.ts +18 -53
  139. package/src/interfaces/utils.ts +2 -0
  140. package/src/lmdb/array.ts +2 -1
  141. package/src/lmdb/index.ts +11 -6
  142. package/src/lmdb/map.ts +23 -94
  143. package/src/lmdb/multi_map.ts +51 -0
  144. package/src/lmdb/store.ts +24 -48
  145. package/src/lmdb-v2/array.ts +8 -3
  146. package/src/lmdb-v2/factory.ts +29 -18
  147. package/src/lmdb-v2/map.ts +30 -127
  148. package/src/lmdb-v2/message.ts +23 -0
  149. package/src/lmdb-v2/multi_map.ts +150 -0
  150. package/src/lmdb-v2/read_transaction.ts +40 -0
  151. package/src/lmdb-v2/set.ts +33 -0
  152. package/src/lmdb-v2/singleton.ts +6 -2
  153. package/src/lmdb-v2/store.ts +24 -42
  154. package/src/lmdb-v2/tx-helpers.ts +29 -0
  155. package/src/lmdb-v2/utils.ts +0 -118
  156. package/src/lmdb-v2/write_transaction.ts +11 -10
  157. package/src/stores/index.ts +2 -0
  158. package/src/stores/l2_tips_store.ts +67 -49
  159. package/src/utils.ts +79 -21
  160. package/dest/interfaces/store_test_suite.d.ts +0 -3
  161. package/dest/interfaces/store_test_suite.d.ts.map +0 -1
  162. package/dest/interfaces/store_test_suite.js +0 -37
  163. 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;
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,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,9 +1,10 @@
1
1
  {
2
2
  "name": "@aztec/kv-store",
3
- "version": "0.0.0-test.1",
3
+ "version": "0.0.1-commit.0208eb9",
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,49 +12,53 @@
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
- "formatting": "run -T prettier --check ./src && run -T eslint ./src",
18
- "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
19
18
  "test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json",
20
19
  "test:browser": "wtr --config ./web-test-runner.config.mjs",
21
- "test": "yarn test:node && yarn test:browser && true"
20
+ "test": "yarn test:node && yarn test:browser",
21
+ "test:jest": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
22
22
  },
23
23
  "inherits": [
24
24
  "../package.common.json",
25
25
  "./package.local.json"
26
26
  ],
27
27
  "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",
28
+ "@aztec/constants": "0.0.1-commit.0208eb9",
29
+ "@aztec/ethereum": "0.0.1-commit.0208eb9",
30
+ "@aztec/foundation": "0.0.1-commit.0208eb9",
31
+ "@aztec/native": "0.0.1-commit.0208eb9",
32
+ "@aztec/stdlib": "0.0.1-commit.0208eb9",
32
33
  "idb": "^8.0.0",
33
34
  "lmdb": "^3.2.0",
34
35
  "msgpackr": "^1.11.2",
36
+ "ohash": "^2.0.11",
35
37
  "ordered-binary": "^1.5.3"
36
38
  },
37
39
  "devDependencies": {
38
- "@jest/globals": "^29.5.0",
40
+ "@jest/globals": "^30.0.0",
39
41
  "@types/chai": "^5.0.1",
40
42
  "@types/chai-as-promised": "^8.0.1",
41
- "@types/jest": "^29.5.0",
43
+ "@types/jest": "^30.0.0",
42
44
  "@types/mocha": "^10.0.10",
43
45
  "@types/mocha-each": "^2.0.4",
44
- "@types/node": "^18.7.23",
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",
50
+ "@web/dev-server-import-maps": "^0.2.1",
47
51
  "@web/test-runner": "^0.19.0",
48
52
  "@web/test-runner-playwright": "^0.11.0",
49
53
  "chai": "^5.1.2",
50
54
  "chai-as-promised": "^8.0.1",
51
- "jest": "^29.5.0",
55
+ "jest": "^30.0.0",
52
56
  "mocha": "^10.8.2",
53
57
  "mocha-each": "^2.0.1",
54
58
  "sinon": "^19.0.2",
55
59
  "ts-node": "^10.9.1",
56
- "typescript": "^5.0.4"
60
+ "typescript": "^5.3.3",
61
+ "util": "^0.12.5"
57
62
  },
58
63
  "files": [
59
64
  "dest",
@@ -61,7 +66,7 @@
61
66
  "!*.test.*"
62
67
  ],
63
68
  "engines": {
64
- "node": ">=18"
69
+ "node": ">=20.10"
65
70
  },
66
71
  "jest": {
67
72
  "extensionsToTreatAsEsm": [
@@ -94,6 +99,10 @@
94
99
  "testTimeout": 120000,
95
100
  "setupFiles": [
96
101
  "../../foundation/src/jest/setup.mjs"
102
+ ],
103
+ "testEnvironment": "../../foundation/src/jest/env.mjs",
104
+ "setupFilesAfterEnv": [
105
+ "../../foundation/src/jest/setupAfterEnv.mjs"
97
106
  ]
98
107
  }
99
108
  }
package/src/config.ts CHANGED
@@ -4,7 +4,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
4
4
 
5
5
  export type DataStoreConfig = {
6
6
  dataDirectory: string | undefined;
7
- dataStoreMapSizeKB: number;
7
+ dataStoreMapSizeKb: number;
8
8
  l1Contracts?: { rollupAddress: EthAddress };
9
9
  };
10
10
 
@@ -13,14 +13,16 @@ export const dataConfigMappings: ConfigMappingsType<DataStoreConfig> = {
13
13
  env: 'DATA_DIRECTORY',
14
14
  description: 'Optional dir to store data. If omitted will store in memory.',
15
15
  },
16
- dataStoreMapSizeKB: {
16
+ dataStoreMapSizeKb: {
17
17
  env: 'DATA_STORE_MAP_SIZE_KB',
18
- description: 'DB mapping size to be applied to all key/value stores',
18
+ description: 'The maximum possible size of a data store DB in KB. Can be overridden by component-specific options.',
19
19
  ...numberConfigHelper(128 * 1_024 * 1_024), // Defaulted to 128 GB
20
20
  },
21
21
  l1Contracts: {
22
22
  description: 'The deployed L1 contract addresses',
23
- nested: l1ContractAddressesMapping,
23
+ nested: {
24
+ rollupAddress: l1ContractAddressesMapping.rollupAddress,
25
+ },
24
26
  },
25
27
  };
26
28
 
@@ -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
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.l1Contracts?.rollupAddress, log);
25
27
  }
26
28
 
27
29
  export function openTmpStore(ephemeral: boolean = false): Promise<AztecIndexedDBStore> {
@@ -1,22 +1,23 @@
1
1
  import type { IDBPDatabase, IDBPObjectStore } from 'idb';
2
+ import { hash } from 'ohash';
2
3
 
3
- import type { Key, Range } from '../interfaces/common.js';
4
- import type { AztecAsyncMultiMap } from '../interfaces/map.js';
4
+ import type { Key, Range, Value } from '../interfaces/common.js';
5
+ import type { AztecAsyncMap } from '../interfaces/map.js';
5
6
  import type { AztecIDBSchema } from './store.js';
6
7
 
7
8
  /**
8
9
  * A map backed by IndexedDB.
9
10
  */
10
- export class IndexedDBAztecMap<K extends Key, V> implements AztecAsyncMultiMap<K, V> {
11
+ export class IndexedDBAztecMap<K extends Key, V extends Value> implements AztecAsyncMap<K, V> {
11
12
  protected name: string;
12
- #container: string;
13
+ protected container: string;
13
14
 
14
15
  #_db?: IDBPObjectStore<AztecIDBSchema, ['data'], 'data', 'readwrite'>;
15
16
  #rootDB: IDBPDatabase<AztecIDBSchema>;
16
17
 
17
18
  constructor(rootDB: IDBPDatabase<AztecIDBSchema>, mapName: string) {
18
19
  this.name = mapName;
19
- this.#container = `map:${mapName}`;
20
+ this.container = `map:${mapName}`;
20
21
  this.#rootDB = rootDB;
21
22
  }
22
23
 
@@ -29,41 +30,38 @@ export class IndexedDBAztecMap<K extends Key, V> implements AztecAsyncMultiMap<K
29
30
  }
30
31
 
31
32
  async getAsync(key: K): Promise<V | undefined> {
32
- const data = await this.db.get(this.#slot(key));
33
+ const data = await this.db.get(this.slot(key));
33
34
  return data?.value as V;
34
35
  }
35
36
 
36
- async *getValuesAsync(key: K): AsyncIterableIterator<V> {
37
- const index = this.db.index('keyCount');
38
- const rangeQuery = IDBKeyRange.bound(
39
- [this.#container, this.#normalizeKey(key), 0],
40
- [this.#container, this.#normalizeKey(key), Number.MAX_SAFE_INTEGER],
41
- false,
42
- false,
43
- );
44
- for await (const cursor of index.iterate(rangeQuery)) {
45
- yield cursor.value.value as V;
46
- }
47
- }
48
-
49
37
  async hasAsync(key: K): Promise<boolean> {
50
38
  const result = (await this.getAsync(key)) !== undefined;
51
39
  return result;
52
40
  }
53
41
 
42
+ async sizeAsync(): Promise<number> {
43
+ const index = this.db.index('key');
44
+ const rangeQuery = IDBKeyRange.bound([this.container], [this.container + '\uffff'], true, true);
45
+ return await index.count(rangeQuery);
46
+ }
47
+
54
48
  async set(key: K, val: V): Promise<void> {
55
- const count = await this.db
56
- .index('key')
57
- .count(IDBKeyRange.bound([this.#container, this.#normalizeKey(key)], [this.#container, this.#normalizeKey(key)]));
58
49
  await this.db.put({
59
50
  value: val,
60
- container: this.#container,
61
- key: this.#normalizeKey(key),
62
- keyCount: count + 1,
63
- slot: this.#slot(key, count),
51
+ hash: hash(val),
52
+ container: this.container,
53
+ key: this.normalizeKey(key),
54
+ keyCount: 1,
55
+ slot: this.slot(key),
64
56
  });
65
57
  }
66
58
 
59
+ async setMany(entries: { key: K; value: V }[]): Promise<void> {
60
+ for (const { key, value } of entries) {
61
+ await this.set(key, value);
62
+ }
63
+ }
64
+
67
65
  swap(_key: K, _fn: (val: V | undefined) => V): Promise<void> {
68
66
  throw new Error('Not implemented');
69
67
  }
@@ -77,30 +75,17 @@ export class IndexedDBAztecMap<K extends Key, V> implements AztecAsyncMultiMap<K
77
75
  }
78
76
 
79
77
  async delete(key: K): Promise<void> {
80
- await this.db.delete(this.#slot(key));
81
- }
82
-
83
- async deleteValue(key: K, val: V): Promise<void> {
84
- const index = this.db.index('keyCount');
85
- const rangeQuery = IDBKeyRange.bound(
86
- [this.#container, this.#normalizeKey(key), 0],
87
- [this.#container, this.#normalizeKey(key), Number.MAX_SAFE_INTEGER],
88
- false,
89
- false,
90
- );
91
- for await (const cursor of index.iterate(rangeQuery)) {
92
- if (JSON.stringify(cursor.value.value) === JSON.stringify(val)) {
93
- await cursor.delete();
94
- return;
95
- }
96
- }
78
+ await this.db.delete(this.slot(key));
97
79
  }
98
80
 
99
81
  async *entriesAsync(range: Range<K> = {}): AsyncIterableIterator<[K, V]> {
100
82
  const index = this.db.index('key');
83
+ const startKey = range.start ? this.normalizeKey(range.start) : [];
84
+ const endKey = range.end ? this.normalizeKey(range.end) : ['\uffff'];
85
+
101
86
  const rangeQuery = IDBKeyRange.bound(
102
- [this.#container, range.start ?? ''],
103
- [this.#container, range.end ?? '\uffff'],
87
+ [this.container, startKey],
88
+ [this.container, endKey],
104
89
  !!range.reverse,
105
90
  !range.reverse,
106
91
  );
@@ -109,7 +94,7 @@ export class IndexedDBAztecMap<K extends Key, V> implements AztecAsyncMultiMap<K
109
94
  if (range.limit && count >= range.limit) {
110
95
  return;
111
96
  }
112
- yield [cursor.value.key, cursor.value.value] as [K, V];
97
+ yield [this.#denormalizeKey(cursor.value.key), cursor.value.value] as [K, V];
113
98
  count++;
114
99
  }
115
100
  }
@@ -122,21 +107,19 @@ export class IndexedDBAztecMap<K extends Key, V> implements AztecAsyncMultiMap<K
122
107
 
123
108
  async *keysAsync(range: Range<K> = {}): AsyncIterableIterator<K> {
124
109
  for await (const [key, _] of this.entriesAsync(range)) {
125
- yield this.#denormalizeKey(key as string);
110
+ yield key;
126
111
  }
127
112
  }
128
113
 
129
- #denormalizeKey(key: string): K {
130
- const denormalizedKey = (key as string).split(',').map(part => (isNaN(parseInt(part)) ? part : parseInt(part)));
131
- return (denormalizedKey.length > 1 ? denormalizedKey : key) as K;
114
+ #denormalizeKey(key: (string | number | Uint8Array)[]): K {
115
+ return (key.length > 1 ? key : key[0]) as K;
132
116
  }
133
117
 
134
- #normalizeKey(key: K): string {
135
- const arrayKey = Array.isArray(key) ? key : [key];
136
- return arrayKey.join(',');
118
+ protected normalizeKey(key: K): (string | number | Uint8Array)[] {
119
+ return Array.isArray(key) ? key : [key];
137
120
  }
138
121
 
139
- #slot(key: K, index: number = 0): string {
140
- return `map:${this.name}:slot:${this.#normalizeKey(key)}:${index}`;
122
+ protected slot(key: K, index: number = 0): string {
123
+ return `map:${this.name}:slot:${this.normalizeKey(key)}:${index}`;
141
124
  }
142
125
  }