@aztec/kv-store 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a
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.
- package/README.md +8 -1
- package/dest/indexeddb/index.js +1 -1
- package/dest/interfaces/array_test_suite.d.ts +1 -1
- package/dest/interfaces/array_test_suite.d.ts.map +1 -1
- package/dest/interfaces/array_test_suite.js +33 -34
- package/dest/interfaces/index.d.ts +2 -2
- package/dest/interfaces/index.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.d.ts +1 -1
- package/dest/interfaces/map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.js +32 -33
- package/dest/interfaces/multi_map_test_suite.d.ts +1 -1
- package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/multi_map_test_suite.js +68 -69
- package/dest/interfaces/set_test_suite.d.ts +1 -1
- package/dest/interfaces/set_test_suite.d.ts.map +1 -1
- package/dest/interfaces/set_test_suite.js +13 -14
- package/dest/interfaces/singleton_test_suite.d.ts +1 -1
- package/dest/interfaces/singleton_test_suite.d.ts.map +1 -1
- package/dest/interfaces/singleton_test_suite.js +6 -7
- package/dest/lmdb/index.js +2 -2
- package/dest/lmdb-v2/factory.d.ts +28 -3
- package/dest/lmdb-v2/factory.d.ts.map +1 -1
- package/dest/lmdb-v2/factory.js +61 -10
- package/dest/lmdb-v2/store.d.ts +2 -2
- package/dest/lmdb-v2/store.d.ts.map +1 -1
- package/dest/lmdb-v2/store.js +4 -4
- package/dest/sqlite-opfs/array.js +2 -2
- package/dest/sqlite-opfs/errors.d.ts +27 -0
- package/dest/sqlite-opfs/errors.d.ts.map +1 -0
- package/dest/sqlite-opfs/errors.js +34 -0
- package/dest/sqlite-opfs/index.d.ts +10 -1
- package/dest/sqlite-opfs/index.d.ts.map +1 -1
- package/dest/sqlite-opfs/index.js +10 -1
- package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts +32 -0
- package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts.map +1 -0
- package/dest/sqlite-opfs/internal/ordered-binary-browser.js +448 -0
- package/dest/sqlite-opfs/map.d.ts +2 -2
- package/dest/sqlite-opfs/map.d.ts.map +1 -1
- package/dest/sqlite-opfs/map.js +2 -2
- package/dest/sqlite-opfs/messages.d.ts +9 -1
- package/dest/sqlite-opfs/messages.d.ts.map +1 -1
- package/dest/sqlite-opfs/messages.js +1 -1
- package/dest/sqlite-opfs/singleton.js +2 -2
- package/dest/sqlite-opfs/store.d.ts +11 -2
- package/dest/sqlite-opfs/store.d.ts.map +1 -1
- package/dest/sqlite-opfs/store.js +51 -12
- package/dest/sqlite-opfs/worker.js +63 -8
- package/dest/stores/l2_tips_store.d.ts +3 -3
- package/dest/stores/l2_tips_store.d.ts.map +1 -1
- package/dest/stores/l2_tips_store.js +2 -2
- package/package.json +21 -21
- package/src/indexeddb/index.ts +1 -1
- package/src/interfaces/array_test_suite.ts +33 -35
- package/src/interfaces/index.ts +1 -1
- package/src/interfaces/map_test_suite.ts +32 -34
- package/src/interfaces/multi_map_test_suite.ts +65 -67
- package/src/interfaces/set_test_suite.ts +13 -15
- package/src/interfaces/singleton_test_suite.ts +6 -8
- package/src/lmdb/index.ts +2 -2
- package/src/lmdb-v2/factory.ts +79 -9
- package/src/lmdb-v2/store.ts +4 -2
- package/src/sqlite-opfs/array.ts +2 -2
- package/src/sqlite-opfs/errors.ts +44 -0
- package/src/sqlite-opfs/index.ts +13 -1
- package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -0
- package/src/sqlite-opfs/map.ts +2 -2
- package/src/sqlite-opfs/messages.ts +13 -2
- package/src/sqlite-opfs/singleton.ts +2 -2
- package/src/sqlite-opfs/store.ts +53 -5
- package/src/sqlite-opfs/worker.ts +78 -9
- package/src/stores/l2_tips_store.ts +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference lib="webworker" />
|
|
2
|
-
import sqlite3InitModule, { type Database, type SAHPoolUtil, type Sqlite3Static } from '@
|
|
2
|
+
import sqlite3InitModule, { type Database, type SAHPoolUtil, type Sqlite3Static } from '@aztec/sqlite3mc-wasm';
|
|
3
3
|
|
|
4
|
+
import { SqliteEncryptionError, type SqliteEncryptionErrorCode, isDecryptFailureMessage } from './errors.js';
|
|
4
5
|
import type { ResultRow, SqlValue, WorkerRequest, WorkerResponse } from './messages.js';
|
|
5
6
|
|
|
6
7
|
const SCHEMA_SQL = `
|
|
@@ -20,6 +21,7 @@ const SCHEMA_SQL = `
|
|
|
20
21
|
|
|
21
22
|
const DEFAULT_SAH_POOL_DIRECTORY = '.aztec-kv';
|
|
22
23
|
const SAH_POOL_VFS_NAME = 'aztec-kv-opfs';
|
|
24
|
+
const MC_SAH_POOL_VFS_NAME = `multipleciphers-${SAH_POOL_VFS_NAME}`;
|
|
23
25
|
|
|
24
26
|
let sqlite3: Sqlite3Static | undefined;
|
|
25
27
|
let pool: SAHPoolUtil | undefined;
|
|
@@ -28,24 +30,63 @@ let dbPath: string | undefined;
|
|
|
28
30
|
|
|
29
31
|
async function ensurePool(directory: string): Promise<SAHPoolUtil> {
|
|
30
32
|
sqlite3 ??= await sqlite3InitModule();
|
|
33
|
+
const s = sqlite3;
|
|
31
34
|
if (!pool) {
|
|
32
|
-
pool = await
|
|
35
|
+
pool = await s.installOpfsSAHPoolVfs({
|
|
33
36
|
name: SAH_POOL_VFS_NAME,
|
|
34
37
|
directory,
|
|
35
38
|
initialCapacity: 8,
|
|
36
39
|
});
|
|
40
|
+
// Register a sqlite3mc-wrapped VFS pointing at our SAH Pool VFS.
|
|
41
|
+
// Encrypted DBs must be opened through this wrapper so sqlite3mc can
|
|
42
|
+
// intercept file I/O; plain DBs continue using the SAH Pool VFS directly.
|
|
43
|
+
// The wrapper name is `multipleciphers-<underlying>`.
|
|
44
|
+
(s.capi as unknown as { sqlite3mc_vfs_create(name: string, makeDefault: number): number }).sqlite3mc_vfs_create(
|
|
45
|
+
SAH_POOL_VFS_NAME,
|
|
46
|
+
0,
|
|
47
|
+
);
|
|
37
48
|
}
|
|
38
|
-
return pool
|
|
49
|
+
return pool!;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Applies sqlite3mc's ChaCha20 page cipher using a pre-derived 32-byte key.
|
|
54
|
+
* The PRAGMAs must run before any schema DDL so sqlite3mc can decrypt existing
|
|
55
|
+
* pages and encrypt new ones. Zeroes the caller-held key array after the PRAGMA
|
|
56
|
+
* completes to minimize residency of the raw key bytes outside sqlite3mc's heap.
|
|
57
|
+
*/
|
|
58
|
+
function applyEncryptionKey(conn: Database, key: Uint8Array): void {
|
|
59
|
+
const hex = Array.from(key, b => b.toString(16).padStart(2, '0')).join('');
|
|
60
|
+
conn.exec(`PRAGMA cipher = 'chacha20'`);
|
|
61
|
+
conn.exec(`PRAGMA key = "x'${hex}'"`);
|
|
62
|
+
key.fill(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function handleInit(
|
|
66
|
+
dbName: string,
|
|
67
|
+
ephemeral: boolean,
|
|
68
|
+
directory?: string,
|
|
69
|
+
encryptionKey?: Uint8Array,
|
|
70
|
+
): Promise<void> {
|
|
42
71
|
sqlite3 ??= await sqlite3InitModule();
|
|
72
|
+
const s = sqlite3;
|
|
73
|
+
if (encryptionKey !== undefined && ephemeral) {
|
|
74
|
+
throw new SqliteEncryptionError(
|
|
75
|
+
'encryption_not_supported_for_ephemeral',
|
|
76
|
+
'encryptionKey is not supported for ephemeral (:memory:) stores',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
43
79
|
if (ephemeral) {
|
|
44
|
-
db = new
|
|
80
|
+
db = new s.oo1.DB(':memory:', 'c');
|
|
45
81
|
} else {
|
|
46
|
-
|
|
82
|
+
await ensurePool(directory ?? DEFAULT_SAH_POOL_DIRECTORY);
|
|
47
83
|
dbPath = normalizeDbPath(dbName);
|
|
48
|
-
|
|
84
|
+
if (encryptionKey !== undefined) {
|
|
85
|
+
db = new s.oo1.DB({ filename: dbPath, flags: 'c', vfs: MC_SAH_POOL_VFS_NAME });
|
|
86
|
+
applyEncryptionKey(db, encryptionKey);
|
|
87
|
+
} else {
|
|
88
|
+
db = new pool!.OpfsSAHPoolDb(dbPath);
|
|
89
|
+
}
|
|
49
90
|
}
|
|
50
91
|
runSql(SCHEMA_SQL);
|
|
51
92
|
}
|
|
@@ -121,7 +162,7 @@ function respond(msg: WorkerResponse): void {
|
|
|
121
162
|
try {
|
|
122
163
|
switch (req.type) {
|
|
123
164
|
case 'init':
|
|
124
|
-
await handleInit(req.dbName, req.ephemeral, req.poolDirectory);
|
|
165
|
+
await handleInit(req.dbName, req.ephemeral, req.poolDirectory, req.encryptionKey);
|
|
125
166
|
return respond({ type: 'ok', id: req.id });
|
|
126
167
|
case 'close':
|
|
127
168
|
handleClose();
|
|
@@ -157,6 +198,34 @@ function respond(msg: WorkerResponse): void {
|
|
|
157
198
|
}
|
|
158
199
|
} catch (err) {
|
|
159
200
|
const message = err instanceof Error ? err.message : String(err);
|
|
160
|
-
respond({ type: 'err', id: req.id, message });
|
|
201
|
+
respond({ type: 'err', id: req.id, message, encryptionCode: detectEncryptionCode(req, err, message) });
|
|
161
202
|
}
|
|
162
203
|
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Maps a thrown error during request handling to a typed encryption code, so the
|
|
207
|
+
* main thread can re-hydrate it as a {@link SqliteEncryptionError}. Returns
|
|
208
|
+
* `undefined` for non-encryption errors (preserves the existing untyped path).
|
|
209
|
+
*
|
|
210
|
+
* Two sources:
|
|
211
|
+
* - The error was already a `SqliteEncryptionError` (pre-flight throws inside
|
|
212
|
+
* this worker — e.g. ephemeral + encryptionKey). Forward its code as-is.
|
|
213
|
+
* - The error came from SQLite/sqlite3mc with a known decrypt-failure message
|
|
214
|
+
* during an `init` request. Both "wrong key supplied" and "no key supplied
|
|
215
|
+
* to an encrypted DB" surface as SQLITE_NOTADB ("file is not a database") —
|
|
216
|
+
* we don't constrain on `req.encryptionKey` because the no-key-on-encrypted-DB
|
|
217
|
+
* case is exactly when the caller most needs the typed signal.
|
|
218
|
+
*/
|
|
219
|
+
function detectEncryptionCode(
|
|
220
|
+
req: WorkerRequest,
|
|
221
|
+
err: unknown,
|
|
222
|
+
message: string,
|
|
223
|
+
): SqliteEncryptionErrorCode | undefined {
|
|
224
|
+
if (err instanceof SqliteEncryptionError) {
|
|
225
|
+
return err.code;
|
|
226
|
+
}
|
|
227
|
+
if (req.type === 'init' && isDecryptFailureMessage(message)) {
|
|
228
|
+
return 'decrypt_failed';
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block';
|
|
2
|
+
import { type BlockHash, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block';
|
|
3
3
|
import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
4
4
|
|
|
5
5
|
import type { AztecAsyncMap } from '../interfaces/map.js';
|
|
@@ -18,8 +18,9 @@ export class L2TipsKVStore extends L2TipsStoreBase {
|
|
|
18
18
|
constructor(
|
|
19
19
|
private store: AztecAsyncKVStore,
|
|
20
20
|
namespace: string,
|
|
21
|
+
initialBlockHash: BlockHash,
|
|
21
22
|
) {
|
|
22
|
-
super();
|
|
23
|
+
super(initialBlockHash);
|
|
23
24
|
this.l2TipsStore = store.openMap([namespace, 'l2_tips'].join('_'));
|
|
24
25
|
this.l2BlockHashesStore = store.openMap([namespace, 'l2_block_hashes'].join('_'));
|
|
25
26
|
this.l2BlockNumberToCheckpointNumberStore = store.openMap(
|