@aztec/kv-store 0.72.1 → 0.73.0
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/dest/config.js +3 -3
- package/dest/indexeddb/store.d.ts +4 -7
- package/dest/indexeddb/store.d.ts.map +1 -1
- package/dest/indexeddb/store.js +5 -2
- package/dest/interfaces/common.d.ts +6 -1
- package/dest/interfaces/common.d.ts.map +1 -1
- package/dest/interfaces/index.d.ts +1 -1
- package/dest/interfaces/index.d.ts.map +1 -1
- package/dest/interfaces/map.d.ts +0 -6
- package/dest/interfaces/map.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.js +1 -12
- package/dest/interfaces/store.d.ts +11 -11
- package/dest/interfaces/store.d.ts.map +1 -1
- package/dest/lmdb/store.d.ts +2 -6
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +3 -3
- package/dest/lmdb-v2/factory.d.ts +7 -0
- package/dest/lmdb-v2/factory.d.ts.map +1 -0
- package/dest/lmdb-v2/factory.js +56 -0
- package/dest/lmdb-v2/index.d.ts +3 -0
- package/dest/lmdb-v2/index.d.ts.map +1 -0
- package/dest/lmdb-v2/index.js +3 -0
- package/dest/lmdb-v2/map.d.ts +86 -0
- package/dest/lmdb-v2/map.d.ts.map +1 -0
- package/dest/lmdb-v2/map.js +196 -0
- package/dest/lmdb-v2/message.d.ts +112 -0
- package/dest/lmdb-v2/message.d.ts.map +1 -0
- package/dest/lmdb-v2/message.js +19 -0
- package/dest/lmdb-v2/read_transaction.d.ts +14 -0
- package/dest/lmdb-v2/read_transaction.d.ts.map +1 -0
- package/dest/lmdb-v2/read_transaction.js +89 -0
- package/dest/lmdb-v2/singleton.d.ts +12 -0
- package/dest/lmdb-v2/singleton.d.ts.map +1 -0
- package/dest/lmdb-v2/singleton.js +29 -0
- package/dest/lmdb-v2/store.d.ts +41 -0
- package/dest/lmdb-v2/store.d.ts.map +1 -0
- package/dest/lmdb-v2/store.js +156 -0
- package/dest/lmdb-v2/utils.d.ts +19 -0
- package/dest/lmdb-v2/utils.d.ts.map +1 -0
- package/dest/lmdb-v2/utils.js +126 -0
- package/dest/lmdb-v2/write_transaction.d.ts +19 -0
- package/dest/lmdb-v2/write_transaction.d.ts.map +1 -0
- package/dest/lmdb-v2/write_transaction.js +234 -0
- package/dest/stores/l2_tips_store.js +2 -2
- package/package.json +14 -6
- package/src/config.ts +2 -2
- package/src/indexeddb/store.ts +8 -4
- package/src/interfaces/common.ts +3 -1
- package/src/interfaces/index.ts +1 -1
- package/src/interfaces/map.ts +0 -7
- package/src/interfaces/map_test_suite.ts +1 -16
- package/src/interfaces/store.ts +13 -3
- package/src/lmdb/store.ts +4 -4
- package/src/lmdb-v2/factory.ts +79 -0
- package/src/lmdb-v2/index.ts +2 -0
- package/src/lmdb-v2/map.ts +233 -0
- package/src/lmdb-v2/message.ts +146 -0
- package/src/lmdb-v2/read_transaction.ts +116 -0
- package/src/lmdb-v2/singleton.ts +34 -0
- package/src/lmdb-v2/store.ts +210 -0
- package/src/lmdb-v2/utils.ts +150 -0
- package/src/lmdb-v2/write_transaction.ts +314 -0
- package/src/stores/l2_tips_store.ts +1 -1
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/kv-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/interfaces/index.js",
|
|
7
7
|
"./lmdb": "./dest/lmdb/index.js",
|
|
8
|
+
"./lmdb-v2": "./dest/lmdb-v2/index.js",
|
|
8
9
|
"./indexeddb": "./dest/indexeddb/index.js",
|
|
9
10
|
"./stores": "./dest/stores/index.js",
|
|
10
11
|
"./config": "./dest/config.js"
|
|
@@ -12,23 +13,28 @@
|
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "yarn clean && tsc -b",
|
|
14
15
|
"build:dev": "tsc -b --watch",
|
|
16
|
+
"clean:cpp": "rm -rf $(git rev-parse --show-toplevel)/barretenberg/cpp/build-pic",
|
|
15
17
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
16
18
|
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
|
|
17
19
|
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
|
|
18
20
|
"test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json --reporter dot",
|
|
19
21
|
"test:browser": "wtr --config ./web-test-runner.config.mjs",
|
|
20
|
-
"test": "yarn test:node && yarn test:browser && true"
|
|
22
|
+
"test": "yarn test:node && yarn test:browser && true",
|
|
23
|
+
"generate": "mkdir -p build && cp -v ../../barretenberg/cpp/build-pic/lib/nodejs_module.node build"
|
|
21
24
|
},
|
|
22
25
|
"inherits": [
|
|
23
26
|
"../package.common.json",
|
|
24
27
|
"./package.local.json"
|
|
25
28
|
],
|
|
26
29
|
"dependencies": {
|
|
27
|
-
"@aztec/circuit-types": "0.
|
|
28
|
-
"@aztec/ethereum": "0.
|
|
29
|
-
"@aztec/foundation": "0.
|
|
30
|
+
"@aztec/circuit-types": "0.73.0",
|
|
31
|
+
"@aztec/ethereum": "0.73.0",
|
|
32
|
+
"@aztec/foundation": "0.73.0",
|
|
33
|
+
"@aztec/native": "0.73.0",
|
|
30
34
|
"idb": "^8.0.0",
|
|
31
|
-
"lmdb": "^3.2.0"
|
|
35
|
+
"lmdb": "^3.2.0",
|
|
36
|
+
"msgpackr": "^1.11.2",
|
|
37
|
+
"ordered-binary": "^1.5.3"
|
|
32
38
|
},
|
|
33
39
|
"devDependencies": {
|
|
34
40
|
"@aztec/circuits.js": "workspace:^",
|
|
@@ -39,6 +45,7 @@
|
|
|
39
45
|
"@types/mocha": "^10.0.10",
|
|
40
46
|
"@types/mocha-each": "^2.0.4",
|
|
41
47
|
"@types/node": "^18.7.23",
|
|
48
|
+
"@types/sinon": "^17.0.3",
|
|
42
49
|
"@web/dev-server-esbuild": "^1.0.3",
|
|
43
50
|
"@web/test-runner": "^0.19.0",
|
|
44
51
|
"@web/test-runner-playwright": "^0.11.0",
|
|
@@ -47,6 +54,7 @@
|
|
|
47
54
|
"jest": "^29.5.0",
|
|
48
55
|
"mocha": "^10.8.2",
|
|
49
56
|
"mocha-each": "^2.0.1",
|
|
57
|
+
"sinon": "^19.0.2",
|
|
50
58
|
"ts-node": "^10.9.1",
|
|
51
59
|
"typescript": "^5.0.4"
|
|
52
60
|
},
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l1ContractAddressesMapping } from '@aztec/ethereum';
|
|
1
|
+
import { l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresses';
|
|
2
2
|
import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
|
|
3
3
|
import { type EthAddress } from '@aztec/foundation/eth-address';
|
|
4
4
|
|
|
@@ -20,7 +20,7 @@ export const dataConfigMappings: ConfigMappingsType<DataStoreConfig> = {
|
|
|
20
20
|
},
|
|
21
21
|
l1Contracts: {
|
|
22
22
|
description: 'The deployed L1 contract addresses',
|
|
23
|
-
|
|
23
|
+
nested: l1ContractAddressesMapping,
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
package/src/indexeddb/store.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type Logger } from '@aztec/foundation/log';
|
|
|
3
3
|
import { type DBSchema, type IDBPDatabase, deleteDB, openDB } from 'idb';
|
|
4
4
|
|
|
5
5
|
import { type AztecAsyncArray } from '../interfaces/array.js';
|
|
6
|
-
import { type Key } from '../interfaces/common.js';
|
|
6
|
+
import { type Key, type StoreSize } from '../interfaces/common.js';
|
|
7
7
|
import { type AztecAsyncCounter } from '../interfaces/counter.js';
|
|
8
8
|
import { type AztecAsyncMap, type AztecAsyncMultiMap } from '../interfaces/map.js';
|
|
9
9
|
import { type AztecAsyncSet } from '../interfaces/set.js';
|
|
@@ -124,7 +124,7 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
124
124
|
return multimap;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
openCounter<K extends Key
|
|
127
|
+
openCounter<K extends Key>(_name: string): AztecAsyncCounter<K> {
|
|
128
128
|
throw new Error('Method not implemented.');
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -187,7 +187,11 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
187
187
|
return deleteDB(this.#name);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
estimateSize():
|
|
191
|
-
return { mappingSize: 0, actualSize: 0, numItems: 0 };
|
|
190
|
+
estimateSize(): Promise<StoreSize> {
|
|
191
|
+
return Promise.resolve({ mappingSize: 0, actualSize: 0, numItems: 0 });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
close(): Promise<void> {
|
|
195
|
+
return Promise.resolve();
|
|
192
196
|
}
|
|
193
197
|
}
|
package/src/interfaces/common.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The key type for use with the kv-store
|
|
3
3
|
*/
|
|
4
|
-
export type Key = string | number
|
|
4
|
+
export type Key = string | number;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A range of keys to iterate over.
|
|
@@ -16,3 +16,5 @@ export type Range<K extends Key = Key> = {
|
|
|
16
16
|
/** The maximum number of items to iterate over */
|
|
17
17
|
limit?: number;
|
|
18
18
|
};
|
|
19
|
+
|
|
20
|
+
export type StoreSize = { mappingSize: number; actualSize: number; numItems: number };
|
package/src/interfaces/index.ts
CHANGED
package/src/interfaces/map.ts
CHANGED
|
@@ -11,13 +11,6 @@ interface AztecBaseMap<K extends Key, V> {
|
|
|
11
11
|
*/
|
|
12
12
|
set(key: K, val: V): Promise<void>;
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Atomically swap the value at the given key
|
|
16
|
-
* @param key - The key to swap the value at
|
|
17
|
-
* @param fn - The function to swap the value with
|
|
18
|
-
*/
|
|
19
|
-
swap(key: K, fn: (val: V | undefined) => V): Promise<void>;
|
|
20
|
-
|
|
21
14
|
/**
|
|
22
15
|
* Sets the value at the given key if it does not already exist.
|
|
23
16
|
* @param key - The key to set the value at
|
|
@@ -18,7 +18,7 @@ export function describeAztecMap(
|
|
|
18
18
|
|
|
19
19
|
beforeEach(async () => {
|
|
20
20
|
store = await getStore();
|
|
21
|
-
map = store.openMultiMap<string
|
|
21
|
+
map = store.openMultiMap<string, string>('test');
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
afterEach(async () => {
|
|
@@ -125,21 +125,6 @@ export function describeAztecMap(
|
|
|
125
125
|
expect(await getValues('foo')).to.deep.equal(['baz']);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
-
it('supports tuple keys', async () => {
|
|
129
|
-
// Use a new map because key structure has changed
|
|
130
|
-
const tupleMap = store.openMap<[number, string], string>('test-tuple');
|
|
131
|
-
|
|
132
|
-
await tupleMap.set([5, 'bar'], 'val');
|
|
133
|
-
await tupleMap.set([0, 'foo'], 'val');
|
|
134
|
-
|
|
135
|
-
expect(await keys(undefined, tupleMap)).to.deep.equal([
|
|
136
|
-
[0, 'foo'],
|
|
137
|
-
[5, 'bar'],
|
|
138
|
-
]);
|
|
139
|
-
|
|
140
|
-
expect(await get([5, 'bar'], tupleMap)).to.equal('val');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
128
|
it('supports range queries', async () => {
|
|
144
129
|
await map.set('a', 'a');
|
|
145
130
|
await map.set('b', 'b');
|
package/src/interfaces/store.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AztecArray, type AztecAsyncArray } from './array.js';
|
|
2
|
-
import { type Key } from './common.js';
|
|
2
|
+
import { type Key, type StoreSize } from './common.js';
|
|
3
3
|
import { type AztecAsyncCounter, type AztecCounter } from './counter.js';
|
|
4
4
|
import {
|
|
5
5
|
type AztecAsyncMap,
|
|
@@ -94,7 +94,12 @@ export interface AztecKVStore {
|
|
|
94
94
|
/**
|
|
95
95
|
* Estimates the size of the store in bytes.
|
|
96
96
|
*/
|
|
97
|
-
estimateSize():
|
|
97
|
+
estimateSize(): Promise<StoreSize>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Closes the store
|
|
101
|
+
*/
|
|
102
|
+
close(): Promise<void>;
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
export interface AztecAsyncKVStore {
|
|
@@ -163,5 +168,10 @@ export interface AztecAsyncKVStore {
|
|
|
163
168
|
/**
|
|
164
169
|
* Estimates the size of the store in bytes.
|
|
165
170
|
*/
|
|
166
|
-
estimateSize():
|
|
171
|
+
estimateSize(): Promise<StoreSize>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Closes the store
|
|
175
|
+
*/
|
|
176
|
+
close(): Promise<void>;
|
|
167
177
|
}
|
package/src/lmdb/store.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { tmpdir } from 'os';
|
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
|
|
9
9
|
import { type AztecArray, type AztecAsyncArray } from '../interfaces/array.js';
|
|
10
|
-
import { type Key } from '../interfaces/common.js';
|
|
10
|
+
import { type Key, type StoreSize } from '../interfaces/common.js';
|
|
11
11
|
import { type AztecAsyncCounter, type AztecCounter } from '../interfaces/counter.js';
|
|
12
12
|
import {
|
|
13
13
|
type AztecAsyncMap,
|
|
@@ -216,7 +216,7 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
estimateSize():
|
|
219
|
+
estimateSize(): Promise<StoreSize> {
|
|
220
220
|
const stats = this.#rootDb.getStats();
|
|
221
221
|
// The 'mapSize' is the total amount of virtual address space allocated to the DB (effectively the maximum possible size)
|
|
222
222
|
// http://www.lmdb.tech/doc/group__mdb.html#a4bde3c8b676457342cba2fe27aed5fbd
|
|
@@ -226,11 +226,11 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
226
226
|
}
|
|
227
227
|
const dataResult = this.estimateSubDBSize(this.#data);
|
|
228
228
|
const multiResult = this.estimateSubDBSize(this.#multiMapData);
|
|
229
|
-
return {
|
|
229
|
+
return Promise.resolve({
|
|
230
230
|
mappingSize: mapSize,
|
|
231
231
|
actualSize: dataResult.actualSize + multiResult.actualSize,
|
|
232
232
|
numItems: dataResult.numItems + multiResult.numItems,
|
|
233
|
-
};
|
|
233
|
+
});
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
private estimateSubDBSize(db: Database<unknown, Key>): { actualSize: number; numItems: number } {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/circuits.js';
|
|
2
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
|
|
4
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
import { type DataStoreConfig } from '../config.js';
|
|
9
|
+
import { AztecLMDBStoreV2 } from './store.js';
|
|
10
|
+
|
|
11
|
+
const ROLLUP_ADDRESS_FILE = 'rollup_address';
|
|
12
|
+
const MAX_READERS = 16;
|
|
13
|
+
|
|
14
|
+
export async function createStore(
|
|
15
|
+
name: string,
|
|
16
|
+
config: DataStoreConfig,
|
|
17
|
+
log: Logger = createLogger('kv-store:lmdb-v2:' + name),
|
|
18
|
+
): Promise<AztecLMDBStoreV2> {
|
|
19
|
+
const { dataDirectory, l1Contracts } = config;
|
|
20
|
+
|
|
21
|
+
let store: AztecLMDBStoreV2;
|
|
22
|
+
if (typeof dataDirectory !== 'undefined') {
|
|
23
|
+
const subDir = join(dataDirectory, name);
|
|
24
|
+
await mkdir(subDir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
if (l1Contracts) {
|
|
27
|
+
const { rollupAddress } = l1Contracts;
|
|
28
|
+
const localRollupAddress = await readFile(join(subDir, ROLLUP_ADDRESS_FILE), 'utf-8')
|
|
29
|
+
.then(EthAddress.fromString)
|
|
30
|
+
.catch(() => EthAddress.ZERO);
|
|
31
|
+
|
|
32
|
+
if (!localRollupAddress.equals(rollupAddress)) {
|
|
33
|
+
if (!localRollupAddress.isZero()) {
|
|
34
|
+
log.warn(`Rollup address mismatch. Clearing entire database...`, {
|
|
35
|
+
expected: rollupAddress,
|
|
36
|
+
found: localRollupAddress,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await rm(subDir, { recursive: true, force: true });
|
|
40
|
+
await mkdir(subDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await writeFile(join(subDir, ROLLUP_ADDRESS_FILE), rollupAddress.toString());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
log.info(
|
|
48
|
+
`Creating ${name} data store at directory ${subDir} with map size ${config.dataStoreMapSizeKB} KB (LMDB v2)`,
|
|
49
|
+
);
|
|
50
|
+
store = await AztecLMDBStoreV2.new(subDir, config.dataStoreMapSizeKB, MAX_READERS, () => Promise.resolve(), log);
|
|
51
|
+
} else {
|
|
52
|
+
store = await openTmpStore(name, true, config.dataStoreMapSizeKB, MAX_READERS, log);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return store;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function openTmpStore(
|
|
59
|
+
name: string,
|
|
60
|
+
ephemeral: boolean = true,
|
|
61
|
+
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
62
|
+
maxReaders = MAX_READERS,
|
|
63
|
+
log: Logger = createLogger('kv-store:lmdb-v2:' + name),
|
|
64
|
+
): Promise<AztecLMDBStoreV2> {
|
|
65
|
+
const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
|
|
66
|
+
log.debug(`Created temporary data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
|
|
67
|
+
|
|
68
|
+
// pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
|
|
69
|
+
const cleanup = async () => {
|
|
70
|
+
if (ephemeral) {
|
|
71
|
+
await rm(dataDir, { recursive: true, force: true });
|
|
72
|
+
log.debug(`Deleted temporary data store: ${dataDir}`);
|
|
73
|
+
} else {
|
|
74
|
+
log.debug(`Leaving temporary data store: ${dataDir}`);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, log);
|
|
79
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { Encoder } from 'msgpackr';
|
|
2
|
+
|
|
3
|
+
import type { Key, Range } from '../interfaces/common.js';
|
|
4
|
+
import type { AztecAsyncMap, AztecAsyncMultiMap } from '../interfaces/map.js';
|
|
5
|
+
import { type ReadTransaction } from './read_transaction.js';
|
|
6
|
+
import { type AztecLMDBStoreV2, execInReadTx, execInWriteTx } from './store.js';
|
|
7
|
+
import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
|
|
8
|
+
|
|
9
|
+
export class LMDBMap<K extends Key, V> implements AztecAsyncMap<K, V> {
|
|
10
|
+
private prefix: string;
|
|
11
|
+
private encoder = new Encoder();
|
|
12
|
+
|
|
13
|
+
constructor(private store: AztecLMDBStoreV2, name: string) {
|
|
14
|
+
this.prefix = `map:${name}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sets the value at the given key.
|
|
18
|
+
* @param key - The key to set the value at
|
|
19
|
+
* @param val - The value to set
|
|
20
|
+
*/
|
|
21
|
+
set(key: K, val: V): Promise<void> {
|
|
22
|
+
return execInWriteTx(this.store, tx => tx.set(serializeKey(this.prefix, key), this.encoder.pack(val)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sets the value at the given key if it does not already exist.
|
|
27
|
+
* @param key - The key to set the value at
|
|
28
|
+
* @param val - The value to set
|
|
29
|
+
*/
|
|
30
|
+
setIfNotExists(key: K, val: V): Promise<boolean> {
|
|
31
|
+
return execInWriteTx(this.store, async tx => {
|
|
32
|
+
const strKey = serializeKey(this.prefix, key);
|
|
33
|
+
const exists = !!(await tx.get(strKey));
|
|
34
|
+
if (!exists) {
|
|
35
|
+
await tx.set(strKey, this.encoder.pack(val));
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Deletes the value at the given key.
|
|
44
|
+
* @param key - The key to delete the value at
|
|
45
|
+
*/
|
|
46
|
+
delete(key: K): Promise<void> {
|
|
47
|
+
return execInWriteTx(this.store, tx => tx.remove(serializeKey(this.prefix, key)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getAsync(key: K): Promise<V | undefined> {
|
|
51
|
+
return execInReadTx(this.store, async tx => {
|
|
52
|
+
const val = await tx.get(serializeKey(this.prefix, key));
|
|
53
|
+
return val ? this.encoder.unpack(val) : undefined;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
hasAsync(key: K): Promise<boolean> {
|
|
58
|
+
return execInReadTx(this.store, async tx => !!(await tx.get(serializeKey(this.prefix, key))));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Iterates over the map's key-value entries in the key's natural order
|
|
63
|
+
* @param range - The range of keys to iterate over
|
|
64
|
+
*/
|
|
65
|
+
async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
|
|
66
|
+
const reverse = range?.reverse ?? false;
|
|
67
|
+
const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
|
|
68
|
+
|
|
69
|
+
const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
|
|
70
|
+
|
|
71
|
+
let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
|
|
72
|
+
const shouldClose = !tx;
|
|
73
|
+
tx ??= this.store.getReadTx();
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
for await (const [key, val] of tx.iterate(
|
|
77
|
+
reverse ? endKey! : startKey,
|
|
78
|
+
reverse ? startKey : endKey,
|
|
79
|
+
reverse,
|
|
80
|
+
range?.limit,
|
|
81
|
+
)) {
|
|
82
|
+
const deserializedKey = deserializeKey<K>(this.prefix, key);
|
|
83
|
+
if (!deserializedKey) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
yield [deserializedKey, this.encoder.unpack(val)];
|
|
87
|
+
}
|
|
88
|
+
} finally {
|
|
89
|
+
if (shouldClose) {
|
|
90
|
+
tx.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Iterates over the map's values in the key's natural order
|
|
97
|
+
* @param range - The range of keys to iterate over
|
|
98
|
+
*/
|
|
99
|
+
async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
|
|
100
|
+
for await (const [_, value] of this.entriesAsync(range)) {
|
|
101
|
+
yield value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Iterates over the map's keys in the key's natural order
|
|
107
|
+
* @param range - The range of keys to iterate over
|
|
108
|
+
*/
|
|
109
|
+
async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
|
|
110
|
+
for await (const [key, _] of this.entriesAsync(range)) {
|
|
111
|
+
yield key;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class LMDBMultiMap<K extends Key, V> implements AztecAsyncMultiMap<K, V> {
|
|
117
|
+
private prefix: string;
|
|
118
|
+
private encoder = new Encoder();
|
|
119
|
+
constructor(private store: AztecLMDBStoreV2, name: string) {
|
|
120
|
+
this.prefix = `multimap:${name}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Sets the value at the given key.
|
|
125
|
+
* @param key - The key to set the value at
|
|
126
|
+
* @param val - The value to set
|
|
127
|
+
*/
|
|
128
|
+
set(key: K, val: V): Promise<void> {
|
|
129
|
+
return execInWriteTx(this.store, tx => tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sets the value at the given key if it does not already exist.
|
|
134
|
+
* @param key - The key to set the value at
|
|
135
|
+
* @param val - The value to set
|
|
136
|
+
*/
|
|
137
|
+
setIfNotExists(key: K, val: V): Promise<boolean> {
|
|
138
|
+
return execInWriteTx(this.store, async tx => {
|
|
139
|
+
const exists = !!(await this.getAsync(key));
|
|
140
|
+
if (!exists) {
|
|
141
|
+
await tx.setIndex(serializeKey(this.prefix, key), this.encoder.pack(val));
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Deletes the value at the given key.
|
|
150
|
+
* @param key - The key to delete the value at
|
|
151
|
+
*/
|
|
152
|
+
delete(key: K): Promise<void> {
|
|
153
|
+
return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key)));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getAsync(key: K): Promise<V | undefined> {
|
|
157
|
+
return execInReadTx(this.store, async tx => {
|
|
158
|
+
const val = await tx.getIndex(serializeKey(this.prefix, key));
|
|
159
|
+
return val.length > 0 ? this.encoder.unpack(val[0]) : undefined;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
hasAsync(key: K): Promise<boolean> {
|
|
164
|
+
return execInReadTx(this.store, async tx => (await tx.getIndex(serializeKey(this.prefix, key))).length > 0);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Iterates over the map's key-value entries in the key's natural order
|
|
169
|
+
* @param range - The range of keys to iterate over
|
|
170
|
+
*/
|
|
171
|
+
async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
|
|
172
|
+
const reverse = range?.reverse ?? false;
|
|
173
|
+
const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
|
|
174
|
+
const endKey = range?.end ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
|
|
175
|
+
|
|
176
|
+
let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
|
|
177
|
+
const shouldClose = !tx;
|
|
178
|
+
tx ??= this.store.getReadTx();
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
for await (const [key, vals] of tx.iterateIndex(
|
|
182
|
+
reverse ? endKey! : startKey,
|
|
183
|
+
reverse ? startKey : endKey,
|
|
184
|
+
reverse,
|
|
185
|
+
range?.limit,
|
|
186
|
+
)) {
|
|
187
|
+
const deserializedKey = deserializeKey<K>(this.prefix, key);
|
|
188
|
+
if (!deserializedKey) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
for (const val of vals) {
|
|
193
|
+
yield [deserializedKey, this.encoder.unpack(val)];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} finally {
|
|
197
|
+
if (shouldClose) {
|
|
198
|
+
tx.close();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Iterates over the map's values in the key's natural order
|
|
205
|
+
* @param range - The range of keys to iterate over
|
|
206
|
+
*/
|
|
207
|
+
async *valuesAsync(range?: Range<K>): AsyncIterableIterator<V> {
|
|
208
|
+
for await (const [_, value] of this.entriesAsync(range)) {
|
|
209
|
+
yield value;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Iterates over the map's keys in the key's natural order
|
|
215
|
+
* @param range - The range of keys to iterate over
|
|
216
|
+
*/
|
|
217
|
+
async *keysAsync(range?: Range<K>): AsyncIterableIterator<K> {
|
|
218
|
+
for await (const [key, _] of this.entriesAsync(range)) {
|
|
219
|
+
yield key;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
deleteValue(key: K, val: V | undefined): Promise<void> {
|
|
224
|
+
return execInWriteTx(this.store, tx => tx.removeIndex(serializeKey(this.prefix, key), this.encoder.pack(val)));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async *getValuesAsync(key: K): AsyncIterableIterator<V> {
|
|
228
|
+
const values = await execInReadTx(this.store, tx => tx.getIndex(serializeKey(this.prefix, key)));
|
|
229
|
+
for (const value of values) {
|
|
230
|
+
yield this.encoder.unpack(value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|