@aztec-labs/kv-store 6.0.0-nightly.20260829

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 (246) hide show
  1. package/README.md +19 -0
  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/deprecated/indexeddb/array.d.ts +22 -0
  6. package/dest/deprecated/indexeddb/array.d.ts.map +1 -0
  7. package/dest/deprecated/indexeddb/array.js +121 -0
  8. package/dest/deprecated/indexeddb/index.d.ts +7 -0
  9. package/dest/deprecated/indexeddb/index.d.ts.map +1 -0
  10. package/dest/deprecated/indexeddb/index.js +8 -0
  11. package/dest/deprecated/indexeddb/map.d.ts +38 -0
  12. package/dest/deprecated/indexeddb/map.d.ts.map +1 -0
  13. package/dest/deprecated/indexeddb/map.js +121 -0
  14. package/dest/deprecated/indexeddb/multi_map.d.ts +14 -0
  15. package/dest/deprecated/indexeddb/multi_map.d.ts.map +1 -0
  16. package/dest/deprecated/indexeddb/multi_map.js +108 -0
  17. package/dest/deprecated/indexeddb/set.d.ts +17 -0
  18. package/dest/deprecated/indexeddb/set.d.ts.map +1 -0
  19. package/dest/deprecated/indexeddb/set.js +24 -0
  20. package/dest/deprecated/indexeddb/singleton.d.ts +17 -0
  21. package/dest/deprecated/indexeddb/singleton.d.ts.map +1 -0
  22. package/dest/deprecated/indexeddb/singleton.js +41 -0
  23. package/dest/deprecated/indexeddb/store.d.ts +98 -0
  24. package/dest/deprecated/indexeddb/store.d.ts.map +1 -0
  25. package/dest/deprecated/indexeddb/store.js +171 -0
  26. package/dest/interfaces/array.d.ts +81 -0
  27. package/dest/interfaces/array.d.ts.map +1 -0
  28. package/dest/interfaces/array.js +1 -0
  29. package/dest/interfaces/array_test_suite.d.ts +3 -0
  30. package/dest/interfaces/array_test_suite.d.ts.map +1 -0
  31. package/dest/interfaces/array_test_suite.js +103 -0
  32. package/dest/interfaces/common.d.ts +25 -0
  33. package/dest/interfaces/common.d.ts.map +1 -0
  34. package/dest/interfaces/common.js +8 -0
  35. package/dest/interfaces/counter.d.ts +59 -0
  36. package/dest/interfaces/counter.d.ts.map +1 -0
  37. package/dest/interfaces/counter.js +1 -0
  38. package/dest/interfaces/index.d.ts +10 -0
  39. package/dest/interfaces/index.d.ts.map +1 -0
  40. package/dest/interfaces/index.js +8 -0
  41. package/dest/interfaces/map.d.ts +106 -0
  42. package/dest/interfaces/map.d.ts.map +1 -0
  43. package/dest/interfaces/map.js +3 -0
  44. package/dest/interfaces/map_test_suite.d.ts +3 -0
  45. package/dest/interfaces/map_test_suite.d.ts.map +1 -0
  46. package/dest/interfaces/map_test_suite.js +261 -0
  47. package/dest/interfaces/multi_map.d.ts +41 -0
  48. package/dest/interfaces/multi_map.d.ts.map +1 -0
  49. package/dest/interfaces/multi_map.js +3 -0
  50. package/dest/interfaces/multi_map_test_suite.d.ts +3 -0
  51. package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -0
  52. package/dest/interfaces/multi_map_test_suite.js +363 -0
  53. package/dest/interfaces/set.d.ts +44 -0
  54. package/dest/interfaces/set.d.ts.map +1 -0
  55. package/dest/interfaces/set.js +1 -0
  56. package/dest/interfaces/set_test_suite.d.ts +3 -0
  57. package/dest/interfaces/set_test_suite.d.ts.map +1 -0
  58. package/dest/interfaces/set_test_suite.js +95 -0
  59. package/dest/interfaces/singleton.d.ts +29 -0
  60. package/dest/interfaces/singleton.d.ts.map +1 -0
  61. package/dest/interfaces/singleton.js +4 -0
  62. package/dest/interfaces/singleton_test_suite.d.ts +3 -0
  63. package/dest/interfaces/singleton_test_suite.d.ts.map +1 -0
  64. package/dest/interfaces/singleton_test_suite.js +29 -0
  65. package/dest/interfaces/store.d.ts +120 -0
  66. package/dest/interfaces/store.d.ts.map +1 -0
  67. package/dest/interfaces/store.js +1 -0
  68. package/dest/interfaces/utils.d.ts +18 -0
  69. package/dest/interfaces/utils.d.ts.map +1 -0
  70. package/dest/interfaces/utils.js +18 -0
  71. package/dest/lmdb/array.d.ts +24 -0
  72. package/dest/lmdb/array.d.ts.map +1 -0
  73. package/dest/lmdb/array.js +119 -0
  74. package/dest/lmdb/counter.d.ts +19 -0
  75. package/dest/lmdb/counter.d.ts.map +1 -0
  76. package/dest/lmdb/counter.js +50 -0
  77. package/dest/lmdb/index.d.ts +12 -0
  78. package/dest/lmdb/index.d.ts.map +1 -0
  79. package/dest/lmdb/index.js +25 -0
  80. package/dest/lmdb/map.d.ts +40 -0
  81. package/dest/lmdb/map.d.ts.map +1 -0
  82. package/dest/lmdb/map.js +156 -0
  83. package/dest/lmdb/multi_map.d.ts +13 -0
  84. package/dest/lmdb/multi_map.d.ts.map +1 -0
  85. package/dest/lmdb/multi_map.js +44 -0
  86. package/dest/lmdb/set.d.ts +18 -0
  87. package/dest/lmdb/set.d.ts.map +1 -0
  88. package/dest/lmdb/set.js +32 -0
  89. package/dest/lmdb/singleton.d.ts +14 -0
  90. package/dest/lmdb/singleton.d.ts.map +1 -0
  91. package/dest/lmdb/singleton.js +26 -0
  92. package/dest/lmdb/store.d.ts +94 -0
  93. package/dest/lmdb/store.d.ts.map +1 -0
  94. package/dest/lmdb/store.js +200 -0
  95. package/dest/lmdb-v2/array.d.ts +19 -0
  96. package/dest/lmdb-v2/array.d.ts.map +1 -0
  97. package/dest/lmdb-v2/array.js +103 -0
  98. package/dest/lmdb-v2/factory.d.ts +35 -0
  99. package/dest/lmdb-v2/factory.d.ts.map +1 -0
  100. package/dest/lmdb-v2/factory.js +126 -0
  101. package/dest/lmdb-v2/index.d.ts +3 -0
  102. package/dest/lmdb-v2/index.d.ts.map +1 -0
  103. package/dest/lmdb-v2/index.js +2 -0
  104. package/dest/lmdb-v2/map.d.ts +53 -0
  105. package/dest/lmdb-v2/map.d.ts.map +1 -0
  106. package/dest/lmdb-v2/map.js +106 -0
  107. package/dest/lmdb-v2/message.d.ts +131 -0
  108. package/dest/lmdb-v2/message.d.ts.map +1 -0
  109. package/dest/lmdb-v2/message.js +20 -0
  110. package/dest/lmdb-v2/multi_map.d.ts +52 -0
  111. package/dest/lmdb-v2/multi_map.d.ts.map +1 -0
  112. package/dest/lmdb-v2/multi_map.js +123 -0
  113. package/dest/lmdb-v2/read_transaction.d.ts +16 -0
  114. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -0
  115. package/dest/lmdb-v2/read_transaction.js +137 -0
  116. package/dest/lmdb-v2/set.d.ts +15 -0
  117. package/dest/lmdb-v2/set.d.ts.map +1 -0
  118. package/dest/lmdb-v2/set.js +23 -0
  119. package/dest/lmdb-v2/singleton.d.ts +12 -0
  120. package/dest/lmdb-v2/singleton.d.ts.map +1 -0
  121. package/dest/lmdb-v2/singleton.js +31 -0
  122. package/dest/lmdb-v2/store.d.ts +43 -0
  123. package/dest/lmdb-v2/store.d.ts.map +1 -0
  124. package/dest/lmdb-v2/store.js +171 -0
  125. package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
  126. package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
  127. package/dest/lmdb-v2/tx-helpers.js +21 -0
  128. package/dest/lmdb-v2/utils.d.ts +8 -0
  129. package/dest/lmdb-v2/utils.d.ts.map +1 -0
  130. package/dest/lmdb-v2/utils.js +32 -0
  131. package/dest/lmdb-v2/write_transaction.d.ts +17 -0
  132. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -0
  133. package/dest/lmdb-v2/write_transaction.js +258 -0
  134. package/dest/sqlite-opfs/array.d.ts +21 -0
  135. package/dest/sqlite-opfs/array.d.ts.map +1 -0
  136. package/dest/sqlite-opfs/array.js +128 -0
  137. package/dest/sqlite-opfs/errors.d.ts +54 -0
  138. package/dest/sqlite-opfs/errors.d.ts.map +1 -0
  139. package/dest/sqlite-opfs/errors.js +77 -0
  140. package/dest/sqlite-opfs/index.d.ts +14 -0
  141. package/dest/sqlite-opfs/index.d.ts.map +1 -0
  142. package/dest/sqlite-opfs/index.js +16 -0
  143. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts +32 -0
  144. package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts.map +1 -0
  145. package/dest/sqlite-opfs/internal/ordered-binary-browser.js +448 -0
  146. package/dest/sqlite-opfs/manage.d.ts +22 -0
  147. package/dest/sqlite-opfs/manage.d.ts.map +1 -0
  148. package/dest/sqlite-opfs/manage.js +38 -0
  149. package/dest/sqlite-opfs/map.d.ts +35 -0
  150. package/dest/sqlite-opfs/map.d.ts.map +1 -0
  151. package/dest/sqlite-opfs/map.js +163 -0
  152. package/dest/sqlite-opfs/messages.d.ts +66 -0
  153. package/dest/sqlite-opfs/messages.d.ts.map +1 -0
  154. package/dest/sqlite-opfs/messages.js +5 -0
  155. package/dest/sqlite-opfs/multi_map.d.ts +16 -0
  156. package/dest/sqlite-opfs/multi_map.d.ts.map +1 -0
  157. package/dest/sqlite-opfs/multi_map.js +67 -0
  158. package/dest/sqlite-opfs/pool_integrity.d.ts +20 -0
  159. package/dest/sqlite-opfs/pool_integrity.d.ts.map +1 -0
  160. package/dest/sqlite-opfs/pool_integrity.js +282 -0
  161. package/dest/sqlite-opfs/pool_lock.d.ts +8 -0
  162. package/dest/sqlite-opfs/pool_lock.d.ts.map +1 -0
  163. package/dest/sqlite-opfs/pool_lock.js +47 -0
  164. package/dest/sqlite-opfs/set.d.ts +13 -0
  165. package/dest/sqlite-opfs/set.d.ts.map +1 -0
  166. package/dest/sqlite-opfs/set.js +19 -0
  167. package/dest/sqlite-opfs/singleton.d.ts +13 -0
  168. package/dest/sqlite-opfs/singleton.d.ts.map +1 -0
  169. package/dest/sqlite-opfs/singleton.js +48 -0
  170. package/dest/sqlite-opfs/store.d.ts +84 -0
  171. package/dest/sqlite-opfs/store.d.ts.map +1 -0
  172. package/dest/sqlite-opfs/store.js +318 -0
  173. package/dest/sqlite-opfs/worker.d.ts +2 -0
  174. package/dest/sqlite-opfs/worker.d.ts.map +1 -0
  175. package/dest/sqlite-opfs/worker.js +275 -0
  176. package/dest/stores/index.d.ts +3 -0
  177. package/dest/stores/index.d.ts.map +1 -0
  178. package/dest/stores/index.js +2 -0
  179. package/dest/stores/l2_tips_store.d.ts +23 -0
  180. package/dest/stores/l2_tips_store.d.ts.map +1 -0
  181. package/dest/stores/l2_tips_store.js +64 -0
  182. package/dest/utils.d.ts +15 -0
  183. package/dest/utils.d.ts.map +1 -0
  184. package/dest/utils.js +59 -0
  185. package/package.json +108 -0
  186. package/src/bench/shared_map_bench.ts +111 -0
  187. package/src/deprecated/indexeddb/array.ts +122 -0
  188. package/src/deprecated/indexeddb/index.ts +12 -0
  189. package/src/deprecated/indexeddb/map.ts +137 -0
  190. package/src/deprecated/indexeddb/multi_map.ts +106 -0
  191. package/src/deprecated/indexeddb/set.ts +37 -0
  192. package/src/deprecated/indexeddb/singleton.ts +52 -0
  193. package/src/deprecated/indexeddb/store.ts +214 -0
  194. package/src/interfaces/array.ts +92 -0
  195. package/src/interfaces/array_test_suite.ts +128 -0
  196. package/src/interfaces/common.ts +31 -0
  197. package/src/interfaces/counter.ts +65 -0
  198. package/src/interfaces/index.ts +9 -0
  199. package/src/interfaces/map.ts +117 -0
  200. package/src/interfaces/map_test_suite.ts +198 -0
  201. package/src/interfaces/multi_map.ts +45 -0
  202. package/src/interfaces/multi_map_test_suite.ts +372 -0
  203. package/src/interfaces/set.ts +48 -0
  204. package/src/interfaces/set_test_suite.ts +79 -0
  205. package/src/interfaces/singleton.ts +29 -0
  206. package/src/interfaces/singleton_test_suite.ts +44 -0
  207. package/src/interfaces/store.ts +142 -0
  208. package/src/interfaces/utils.ts +23 -0
  209. package/src/lmdb/array.ts +140 -0
  210. package/src/lmdb/counter.ts +67 -0
  211. package/src/lmdb/index.ts +41 -0
  212. package/src/lmdb/map.ts +177 -0
  213. package/src/lmdb/multi_map.ts +51 -0
  214. package/src/lmdb/set.ts +45 -0
  215. package/src/lmdb/singleton.ts +35 -0
  216. package/src/lmdb/store.ts +242 -0
  217. package/src/lmdb-v2/array.ts +120 -0
  218. package/src/lmdb-v2/factory.ts +184 -0
  219. package/src/lmdb-v2/index.ts +2 -0
  220. package/src/lmdb-v2/map.ts +136 -0
  221. package/src/lmdb-v2/message.ts +169 -0
  222. package/src/lmdb-v2/multi_map.ts +150 -0
  223. package/src/lmdb-v2/read_transaction.ts +156 -0
  224. package/src/lmdb-v2/set.ts +33 -0
  225. package/src/lmdb-v2/singleton.ts +38 -0
  226. package/src/lmdb-v2/store.ts +220 -0
  227. package/src/lmdb-v2/tx-helpers.ts +29 -0
  228. package/src/lmdb-v2/utils.ts +32 -0
  229. package/src/lmdb-v2/write_transaction.ts +315 -0
  230. package/src/sqlite-opfs/array.ts +124 -0
  231. package/src/sqlite-opfs/errors.ts +94 -0
  232. package/src/sqlite-opfs/index.ts +27 -0
  233. package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -0
  234. package/src/sqlite-opfs/manage.ts +46 -0
  235. package/src/sqlite-opfs/map.ts +163 -0
  236. package/src/sqlite-opfs/messages.ts +39 -0
  237. package/src/sqlite-opfs/multi_map.ts +74 -0
  238. package/src/sqlite-opfs/pool_integrity.ts +327 -0
  239. package/src/sqlite-opfs/pool_lock.ts +62 -0
  240. package/src/sqlite-opfs/set.ts +29 -0
  241. package/src/sqlite-opfs/singleton.ts +48 -0
  242. package/src/sqlite-opfs/store.ts +331 -0
  243. package/src/sqlite-opfs/worker.ts +259 -0
  244. package/src/stores/index.ts +3 -0
  245. package/src/stores/l2_tips_store.ts +67 -0
  246. package/src/utils.ts +98 -0
@@ -0,0 +1,184 @@
1
+ import { EthAddress } from '@aztec-labs/foundation/eth-address';
2
+ import { type LoggerBindings, createLogger } from '@aztec-labs/foundation/log';
3
+ import {
4
+ DatabaseVersionManager,
5
+ type SchemaVersionMismatchPolicy,
6
+ type VersionFileReadFailurePolicy,
7
+ } from '@aztec-labs/stdlib/database-version/manager';
8
+ import type { DataStoreConfig } from '@aztec-labs/stdlib/kv-store';
9
+ import { copyFile, mkdir, mkdtemp, rm } from 'fs/promises';
10
+ import { tmpdir } from 'os';
11
+ import { join } from 'path';
12
+
13
+ import { AztecLMDBStoreV2 } from './store.js';
14
+
15
+ const MAX_READERS = 16;
16
+
17
+ /** Optional versioning hooks for persistent LMDB stores. */
18
+ export type CreateStoreOptions = {
19
+ onUpgrade?: (dataDir: string, currentVersion: number, latestVersion: number) => Promise<void>;
20
+ schemaVersionMismatchPolicy?: SchemaVersionMismatchPolicy;
21
+ versionFileReadFailurePolicy?: VersionFileReadFailurePolicy;
22
+ };
23
+
24
+ export async function createStore(
25
+ name: string,
26
+ schemaVersion: number,
27
+ config: DataStoreConfig,
28
+ bindings?: LoggerBindings,
29
+ options: CreateStoreOptions = {},
30
+ ): Promise<AztecLMDBStoreV2> {
31
+ const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
32
+ const { dataDirectory, rollupAddress: rollupFromConfig } = config;
33
+
34
+ let store: AztecLMDBStoreV2;
35
+ if (typeof dataDirectory !== 'undefined') {
36
+ // Get rollup address from contracts config, or use zero address
37
+ const subDir = join(dataDirectory, name);
38
+ await mkdir(subDir, { recursive: true });
39
+
40
+ const rollupAddress = rollupFromConfig ?? EthAddress.ZERO;
41
+
42
+ // Create a version manager
43
+ const versionManager = new DatabaseVersionManager({
44
+ schemaVersion,
45
+ rollupAddress,
46
+ dataDirectory: subDir,
47
+ onOpen: dbDirectory =>
48
+ AztecLMDBStoreV2.new(dbDirectory, config.dataStoreMapSizeKb, MAX_READERS, () => Promise.resolve(), bindings),
49
+ onUpgrade: options.onUpgrade,
50
+ schemaVersionMismatchPolicy: options.schemaVersionMismatchPolicy,
51
+ versionFileReadFailurePolicy: options.versionFileReadFailurePolicy,
52
+ });
53
+
54
+ log.info(
55
+ `Creating ${name} data store at directory ${subDir} with map size ${config.dataStoreMapSizeKb} KB (LMDB v2)`,
56
+ );
57
+ [store] = await versionManager.open();
58
+ } else {
59
+ store = await openTmpStore(name, true, config.dataStoreMapSizeKb, MAX_READERS, bindings);
60
+ }
61
+
62
+ return store;
63
+ }
64
+
65
+ /**
66
+ * Open a persistent on-disk store rooted in OS tmpdir. Caller chooses whether to
67
+ * auto-remove the directory on close. Uses standard durable LMDB flags (fsync on
68
+ * every commit) — for full in-memory / NOSYNC semantics use `openEphemeralStore`.
69
+ */
70
+ export async function openTmpStore(
71
+ name: string,
72
+ cleanupTmpDir: boolean = true,
73
+ dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
74
+ maxReaders = MAX_READERS,
75
+ bindings?: LoggerBindings,
76
+ ): Promise<AztecLMDBStoreV2> {
77
+ const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
78
+ const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
79
+ log.debug(`Created temporary data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
80
+
81
+ // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
82
+ const cleanup = async () => {
83
+ if (cleanupTmpDir) {
84
+ try {
85
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
86
+ log.debug(`Deleted temporary data store: ${dataDir}`);
87
+ } catch (err) {
88
+ log.warn(`Failed to delete temporary data directory (LMDB v2) ${dataDir}: ${err}`);
89
+ }
90
+ } else {
91
+ log.debug(`Leaving temporary data store: ${dataDir}`);
92
+ }
93
+ };
94
+
95
+ return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, false);
96
+ }
97
+
98
+ /**
99
+ * Open a fully-ephemeral store: a fresh tmpdir backing file, LMDB opened with
100
+ * MDB_NOSYNC | MDB_NOMETASYNC (no fsync, kernel-only writeback), and the directory
101
+ * unconditionally removed on close. Intended for tests and worker-local scratch
102
+ * stores where durability is explicitly not desired.
103
+ */
104
+ export async function openEphemeralStore(
105
+ name: string,
106
+ dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
107
+ maxReaders = MAX_READERS,
108
+ bindings?: LoggerBindings,
109
+ ): Promise<AztecLMDBStoreV2> {
110
+ const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
111
+ const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
112
+ log.debug(`Created ephemeral data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
113
+
114
+ const cleanup = async () => {
115
+ try {
116
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
117
+ log.debug(`Deleted ephemeral data store: ${dataDir}`);
118
+ } catch (err) {
119
+ log.warn(`Failed to delete ephemeral data directory (LMDB v2) ${dataDir}: ${err}`);
120
+ }
121
+ };
122
+
123
+ return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, true);
124
+ }
125
+
126
+ export async function openStoreAt(
127
+ dataDir: string,
128
+ dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
129
+ maxReaders = MAX_READERS,
130
+ bindings?: LoggerBindings,
131
+ ): Promise<AztecLMDBStoreV2> {
132
+ const log = createLogger('kv-store:lmdb-v2', bindings);
133
+ log.debug(`Opening data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
134
+ return await AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined, bindings);
135
+ }
136
+
137
+ /**
138
+ * Open a fully-ephemeral store seeded from an existing `data.mdb` file. Creates a fresh tmpdir,
139
+ * copies `srcDataMdbPath` into it as `data.mdb`, opens with MDB_NOSYNC | MDB_NOMETASYNC, and
140
+ * removes the tmpdir on close. Intended for worker clones of a main-thread-built store — the
141
+ * source file is never written to.
142
+ */
143
+ export async function cloneEphemeralStoreFrom(
144
+ srcDataMdbPath: string,
145
+ name: string,
146
+ dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
147
+ maxReaders = MAX_READERS,
148
+ bindings?: LoggerBindings,
149
+ ): Promise<AztecLMDBStoreV2> {
150
+ const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
151
+ const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
152
+ await copyFile(srcDataMdbPath, join(dataDir, 'data.mdb'));
153
+ log.debug(`Cloned ephemeral data store at: ${dataDir} from ${srcDataMdbPath} (LMDB v2)`);
154
+
155
+ const cleanup = async () => {
156
+ try {
157
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
158
+ log.debug(`Deleted ephemeral data store: ${dataDir}`);
159
+ } catch (err) {
160
+ log.warn(`Failed to delete ephemeral data directory (LMDB v2) ${dataDir}: ${err}`);
161
+ }
162
+ };
163
+
164
+ return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, true);
165
+ }
166
+
167
+ export async function openVersionedStoreAt(
168
+ dataDirectory: string,
169
+ schemaVersion: number,
170
+ rollupAddress: EthAddress,
171
+ dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
172
+ maxReaders = MAX_READERS,
173
+ bindings?: LoggerBindings,
174
+ ): Promise<AztecLMDBStoreV2> {
175
+ const log = createLogger('kv-store:lmdb-v2', bindings);
176
+ log.debug(`Opening data store at: ${dataDirectory} with size: ${dbMapSizeKb} KB (LMDB v2)`);
177
+ const [store] = await new DatabaseVersionManager({
178
+ schemaVersion,
179
+ rollupAddress,
180
+ dataDirectory,
181
+ onOpen: dataDir => AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined, bindings),
182
+ }).open();
183
+ return store;
184
+ }
@@ -0,0 +1,2 @@
1
+ export * from './store.js';
2
+ export * from './factory.js';
@@ -0,0 +1,136 @@
1
+ import { Encoder } from 'msgpackr';
2
+
3
+ import type { Key, Range, Value } from '../interfaces/common.js';
4
+ import type { AztecAsyncMap } from '../interfaces/map.js';
5
+ import type { ReadTransaction } from './read_transaction.js';
6
+ import type { AztecLMDBStoreV2 } from './store.js';
7
+ import { execInReadTx, execInWriteTx } from './tx-helpers.js';
8
+ import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
9
+
10
+ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K, V> {
11
+ private prefix: string;
12
+ private encoder = new Encoder();
13
+
14
+ constructor(
15
+ private store: AztecLMDBStoreV2,
16
+ name: string,
17
+ ) {
18
+ this.prefix = `map:${name}`;
19
+ }
20
+ /**
21
+ * Sets the value at the given key.
22
+ * @param key - The key to set the value at
23
+ * @param val - The value to set
24
+ */
25
+ set(key: K, val: V): Promise<void> {
26
+ return execInWriteTx(this.store, tx => tx.set(serializeKey(this.prefix, key), this.encoder.pack(val)));
27
+ }
28
+
29
+ /**
30
+ * Sets the values at the given keys.
31
+ * @param entries - The entries to set
32
+ */
33
+ async setMany(entries: { key: K; value: V }[]): Promise<void> {
34
+ await execInWriteTx(this.store, async tx => {
35
+ for (const { key, value } of entries) {
36
+ await tx.set(serializeKey(this.prefix, key), this.encoder.pack(value));
37
+ }
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Sets the value at the given key if it does not already exist.
43
+ * @param key - The key to set the value at
44
+ * @param val - The value to set
45
+ */
46
+ setIfNotExists(key: K, val: V): Promise<boolean> {
47
+ return execInWriteTx(this.store, async tx => {
48
+ const strKey = serializeKey(this.prefix, key);
49
+ const exists = !!(await tx.get(strKey));
50
+ if (!exists) {
51
+ await tx.set(strKey, this.encoder.pack(val));
52
+ return true;
53
+ }
54
+ return false;
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Deletes the value at the given key.
60
+ * @param key - The key to delete the value at
61
+ */
62
+ delete(key: K): Promise<void> {
63
+ return execInWriteTx(this.store, tx => tx.remove(serializeKey(this.prefix, key)));
64
+ }
65
+
66
+ getAsync(key: K): Promise<V | undefined> {
67
+ return execInReadTx(this.store, async tx => {
68
+ const val = await tx.get(serializeKey(this.prefix, key));
69
+ return val ? this.encoder.unpack(val) : undefined;
70
+ });
71
+ }
72
+
73
+ hasAsync(key: K): Promise<boolean> {
74
+ return execInReadTx(this.store, async tx => !!(await tx.get(serializeKey(this.prefix, key))));
75
+ }
76
+
77
+ sizeAsync(): Promise<number> {
78
+ return execInReadTx(this.store, tx => tx.countEntries(minKey(this.prefix), maxKey(this.prefix), false));
79
+ }
80
+
81
+ /**
82
+ * Iterates over the map's key-value entries in the key's natural order
83
+ * @param range - The range of keys to iterate over
84
+ */
85
+ async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
86
+ const reverse = range?.reverse ?? false;
87
+
88
+ const startKey = range?.start !== undefined ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
89
+
90
+ const endKey =
91
+ range?.end !== undefined ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
92
+
93
+ let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
94
+ const shouldClose = !tx;
95
+ tx ??= this.store.getReadTx();
96
+
97
+ try {
98
+ for await (const [key, val] of tx.iterate(
99
+ reverse ? endKey! : startKey,
100
+ reverse ? startKey : endKey,
101
+ reverse,
102
+ range?.limit,
103
+ )) {
104
+ const deserializedKey = deserializeKey<K>(this.prefix, key);
105
+ if (deserializedKey === false) {
106
+ break;
107
+ }
108
+ yield [deserializedKey, this.encoder.unpack(val)];
109
+ }
110
+ } finally {
111
+ if (shouldClose) {
112
+ tx.close();
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Iterates over the map's values in the key's natural order
119
+ * @param range - The range of keys to iterate over
120
+ */
121
+ async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
122
+ for await (const [_, value] of this.entriesAsync(range)) {
123
+ yield value;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Iterates over the map's keys in the key's natural order
129
+ * @param range - The range of keys to iterate over
130
+ */
131
+ async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
132
+ for await (const [key, _] of this.entriesAsync(range)) {
133
+ yield key;
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,169 @@
1
+ export enum Database {
2
+ DATA = 'data',
3
+ INDEX = 'index',
4
+ }
5
+
6
+ export const CURSOR_PAGE_SIZE = 10;
7
+
8
+ export enum LMDBMessageType {
9
+ OPEN_DATABASE = 100,
10
+ GET,
11
+ HAS,
12
+
13
+ START_CURSOR,
14
+ ADVANCE_CURSOR,
15
+ ADVANCE_CURSOR_COUNT,
16
+ CLOSE_CURSOR,
17
+
18
+ BATCH,
19
+
20
+ STATS,
21
+
22
+ CLOSE,
23
+ COPY_STORE,
24
+ }
25
+
26
+ type Key = Uint8Array;
27
+ type Value = Uint8Array;
28
+ type OptionalValues = Array<Value[] | null>;
29
+ type KeyOptionalValues = [Key, null | Array<Value>];
30
+ type KeyValues = [Key, Value[]];
31
+
32
+ interface OpenDatabaseRequest {
33
+ db: string;
34
+ uniqueKeys?: boolean;
35
+ }
36
+
37
+ interface GetRequest {
38
+ keys: Key[];
39
+ db: string;
40
+ }
41
+
42
+ interface GetResponse {
43
+ values: OptionalValues;
44
+ }
45
+
46
+ interface HasRequest {
47
+ entries: KeyOptionalValues[];
48
+ db: string;
49
+ }
50
+
51
+ interface StartCursorRequest {
52
+ key: Key;
53
+ reverse: boolean;
54
+ count: number | null;
55
+ onePage: boolean | null;
56
+ db: string;
57
+ }
58
+
59
+ interface AdvanceCursorRequest {
60
+ cursor: number;
61
+ count: number | null;
62
+ }
63
+
64
+ interface AdvanceCursorCountRequest {
65
+ cursor: number;
66
+ endKey: Key;
67
+ }
68
+
69
+ interface CloseCursorRequest {
70
+ cursor: number;
71
+ }
72
+
73
+ interface CopyStoreRequest {
74
+ dstPath: string;
75
+ compact: boolean;
76
+ }
77
+
78
+ export interface Batch {
79
+ addEntries: Array<KeyValues>;
80
+ removeEntries: Array<KeyOptionalValues>;
81
+ }
82
+
83
+ interface BatchRequest {
84
+ batches: Map<string, Batch>;
85
+ }
86
+
87
+ export type LMDBRequestBody = {
88
+ [LMDBMessageType.OPEN_DATABASE]: OpenDatabaseRequest;
89
+
90
+ [LMDBMessageType.GET]: GetRequest;
91
+ [LMDBMessageType.HAS]: HasRequest;
92
+
93
+ [LMDBMessageType.START_CURSOR]: StartCursorRequest;
94
+ [LMDBMessageType.ADVANCE_CURSOR]: AdvanceCursorRequest;
95
+ [LMDBMessageType.ADVANCE_CURSOR_COUNT]: AdvanceCursorCountRequest;
96
+ [LMDBMessageType.CLOSE_CURSOR]: CloseCursorRequest;
97
+
98
+ [LMDBMessageType.BATCH]: BatchRequest;
99
+
100
+ [LMDBMessageType.STATS]: void;
101
+
102
+ [LMDBMessageType.CLOSE]: void;
103
+ [LMDBMessageType.COPY_STORE]: CopyStoreRequest;
104
+ };
105
+
106
+ interface GetResponse {
107
+ values: OptionalValues;
108
+ }
109
+
110
+ interface HasResponse {
111
+ exists: boolean[];
112
+ }
113
+
114
+ interface StartCursorResponse {
115
+ cursor: number | null;
116
+ entries: Array<KeyValues>;
117
+ }
118
+
119
+ interface AdvanceCursorResponse {
120
+ entries: Array<KeyValues>;
121
+ done: boolean;
122
+ }
123
+
124
+ interface AdvanceCursorCountResponse {
125
+ count: number;
126
+ done: boolean;
127
+ }
128
+
129
+ interface BatchResponse {
130
+ durationNs: number;
131
+ }
132
+
133
+ interface BoolResponse {
134
+ ok: true;
135
+ }
136
+
137
+ interface StatsResponse {
138
+ stats: Array<{
139
+ name: string;
140
+ numDataItems: bigint | number;
141
+ totalUsedSize: bigint | number;
142
+ }>;
143
+ dbMapSizeBytes: bigint | number;
144
+ dbPhysicalFileSizeBytes: bigint | number;
145
+ }
146
+
147
+ export type LMDBResponseBody = {
148
+ [LMDBMessageType.OPEN_DATABASE]: BoolResponse;
149
+
150
+ [LMDBMessageType.GET]: GetResponse;
151
+ [LMDBMessageType.HAS]: HasResponse;
152
+
153
+ [LMDBMessageType.START_CURSOR]: StartCursorResponse;
154
+ [LMDBMessageType.ADVANCE_CURSOR]: AdvanceCursorResponse;
155
+ [LMDBMessageType.ADVANCE_CURSOR_COUNT]: AdvanceCursorCountResponse;
156
+ [LMDBMessageType.CLOSE_CURSOR]: BoolResponse;
157
+
158
+ [LMDBMessageType.BATCH]: BatchResponse;
159
+
160
+ [LMDBMessageType.STATS]: StatsResponse;
161
+
162
+ [LMDBMessageType.CLOSE]: BoolResponse;
163
+
164
+ [LMDBMessageType.COPY_STORE]: BoolResponse;
165
+ };
166
+
167
+ export interface LMDBMessageChannel {
168
+ sendMessage<T extends LMDBMessageType>(msgType: T, body: LMDBRequestBody[T]): Promise<LMDBResponseBody[T]>;
169
+ }
@@ -0,0 +1,150 @@
1
+ import { Encoder } from 'msgpackr/pack';
2
+ import { MAXIMUM_KEY, toBufferKey } from 'ordered-binary';
3
+
4
+ import type { Key, Range, Value } from '../interfaces/common.js';
5
+ import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
6
+ import type { ReadTransaction } from './read_transaction.js';
7
+ import type { AztecLMDBStoreV2 } from './store.js';
8
+ import { execInReadTx, execInWriteTx } from './tx-helpers.js';
9
+ import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
10
+
11
+ export class LMDBMultiMap<K extends Key, V extends Value> implements AztecAsyncMultiMap<K, V> {
12
+ private prefix: string;
13
+ private encoder = new Encoder();
14
+ constructor(
15
+ private store: AztecLMDBStoreV2,
16
+ name: string,
17
+ ) {
18
+ this.prefix = `multimap:${name}`;
19
+ }
20
+
21
+ /**
22
+ * Sets the value at the given key.
23
+ * @param key - The key to set the value at
24
+ * @param val - The value to set
25
+ */
26
+ set(key: K, val: V): Promise<void> {
27
+ return execInWriteTx(this.store, tx => tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
28
+ }
29
+
30
+ async setMany(entries: { key: K; value: V }[]): Promise<void> {
31
+ await execInWriteTx(this.store, async tx => {
32
+ for (const { key, value } of entries) {
33
+ await tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(value));
34
+ }
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Sets the value at the given key if it does not already exist.
40
+ * @param key - The key to set the value at
41
+ * @param val - The value to set
42
+ */
43
+ setIfNotExists(key: K, val: V): Promise<boolean> {
44
+ return execInWriteTx(this.store, async tx => {
45
+ const exists = !!(await this.getAsync(key));
46
+ if (!exists) {
47
+ await tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val));
48
+ return true;
49
+ }
50
+ return false;
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Deletes the value at the given key.
56
+ * @param key - The key to delete the value at
57
+ */
58
+ delete(key: K): Promise<void> {
59
+ return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key)));
60
+ }
61
+
62
+ getAsync(key: K): Promise<V | undefined> {
63
+ return execInReadTx(this.store, async tx => {
64
+ const val = await tx.getIndex(serializeKey(this.prefix, key));
65
+ return val.length > 0 ? this.encoder.unpack(val[0]) : undefined;
66
+ });
67
+ }
68
+
69
+ hasAsync(key: K): Promise<boolean> {
70
+ return execInReadTx(this.store, async tx => (await tx.getIndex(serializeKey(this.prefix, key))).length > 0);
71
+ }
72
+
73
+ sizeAsync(): Promise<number> {
74
+ return execInReadTx(this.store, tx => tx.countEntriesIndex(minKey(this.prefix), maxKey(this.prefix), false));
75
+ }
76
+
77
+ /**
78
+ * Iterates over the map's key-value entries in the key's natural order
79
+ * @param range - The range of keys to iterate over
80
+ */
81
+ async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
82
+ const reverse = range?.reverse ?? false;
83
+ const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
84
+ const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
85
+
86
+ let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
87
+ const shouldClose = !tx;
88
+ tx ??= this.store.getReadTx();
89
+
90
+ try {
91
+ for await (const [key, vals] of tx.iterateIndex(
92
+ reverse ? endKey! : startKey,
93
+ reverse ? startKey : endKey,
94
+ reverse,
95
+ range?.limit,
96
+ )) {
97
+ const deserializedKey = deserializeKey<K>(this.prefix, key);
98
+ if (!deserializedKey) {
99
+ break;
100
+ }
101
+
102
+ for (const val of vals) {
103
+ yield [deserializedKey, this.encoder.unpack(val)];
104
+ }
105
+ }
106
+ } finally {
107
+ if (shouldClose) {
108
+ tx.close();
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Iterates over the map's values in the key's natural order
115
+ * @param range - The range of keys to iterate over
116
+ */
117
+ async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
118
+ for await (const [_, value] of this.entriesAsync(range)) {
119
+ yield value;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Iterates over the map's keys in the key's natural order
125
+ * @param range - The range of keys to iterate over
126
+ */
127
+ async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
128
+ for await (const [key, _] of this.entriesAsync(range)) {
129
+ yield key;
130
+ }
131
+ }
132
+
133
+ deleteValue(key: K, val: V | undefined): Promise<void> {
134
+ return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
135
+ }
136
+
137
+ async *getValuesAsync(key: K): AsyncIterableIterator<V> {
138
+ const values = await execInReadTx(this.store, tx => tx.getIndex(serializeKey(this.prefix, key)));
139
+ for (const value of values) {
140
+ yield this.encoder.unpack(value);
141
+ }
142
+ }
143
+
144
+ getValueCountAsync(key: K): Promise<number> {
145
+ const startKey = serializeKey(this.prefix, key);
146
+ const endKey = toBufferKey([this.prefix, key, MAXIMUM_KEY]);
147
+
148
+ return execInReadTx(this.store, tx => tx.countEntriesIndex(startKey, endKey, false));
149
+ }
150
+ }