@aztec/kv-store 0.85.0-alpha-testnet.4 → 0.85.0-alpha-testnet.7
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/indexeddb/multi_map.d.ts.map +1 -1
- package/dest/indexeddb/multi_map.js +20 -6
- package/dest/indexeddb/store.d.ts.map +1 -1
- package/dest/indexeddb/store.js +4 -0
- package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/multi_map_test_suite.js +29 -2
- package/package.json +5 -5
- package/src/indexeddb/multi_map.ts +17 -3
- package/src/indexeddb/store.ts +4 -0
- package/src/interfaces/multi_map_test_suite.ts +23 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi_map.d.ts","sourceRoot":"","sources":["../../src/indexeddb/multi_map.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,KAAK,CAChE,SAAQ,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAC9B,YAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC;IAEpB,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"multi_map.d.ts","sourceRoot":"","sources":["../../src/indexeddb/multi_map.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,KAAK,CAChE,SAAQ,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAC9B,YAAW,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC;IAEpB,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC1C,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC;IAcjD,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAejD"}
|
|
@@ -4,6 +4,7 @@ import { IndexedDBAztecMap } from './map.js';
|
|
|
4
4
|
* A multi map backed by IndexedDB.
|
|
5
5
|
*/ export class IndexedDBAztecMultiMap extends IndexedDBAztecMap {
|
|
6
6
|
async set(key, val) {
|
|
7
|
+
// Inserting repeated values is a no-op
|
|
7
8
|
const exists = !!await this.db.index('hash').get(IDBKeyRange.bound([
|
|
8
9
|
this.container,
|
|
9
10
|
this.normalizeKey(key),
|
|
@@ -16,13 +17,23 @@ import { IndexedDBAztecMap } from './map.js';
|
|
|
16
17
|
if (exists) {
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
// Get the maximum keyCount for the given key
|
|
21
|
+
// In order to support sparse multimaps, we cannot rely
|
|
22
|
+
// on just counting the number of entries for the key, since we would repeat slots
|
|
23
|
+
// if we delete an entry
|
|
24
|
+
// set -> container:key:0 (keyCount = 1)
|
|
25
|
+
// set -> container:key:1 (keyCount = 2)
|
|
26
|
+
// delete -> container:key:0 (keyCount = 1)
|
|
27
|
+
// set -> container:key:1 <--- already exists!
|
|
28
|
+
// Instead, we iterate in reverse order to get the last inserted entry
|
|
29
|
+
const index = this.db.index('keyCount');
|
|
30
|
+
const rangeQuery = IDBKeyRange.upperBound([
|
|
23
31
|
this.container,
|
|
24
|
-
this.normalizeKey(key)
|
|
25
|
-
|
|
32
|
+
this.normalizeKey(key),
|
|
33
|
+
Number.MAX_SAFE_INTEGER
|
|
34
|
+
]);
|
|
35
|
+
const maxEntry = (await index.iterate(rangeQuery, 'prevunique').next()).value;
|
|
36
|
+
const count = maxEntry?.value?.keyCount ?? 0;
|
|
26
37
|
await this.db.put({
|
|
27
38
|
value: val,
|
|
28
39
|
hash: hash(val),
|
|
@@ -33,6 +44,7 @@ import { IndexedDBAztecMap } from './map.js';
|
|
|
33
44
|
});
|
|
34
45
|
}
|
|
35
46
|
async *getValuesAsync(key) {
|
|
47
|
+
// Iterate over the whole range of keyCount for the given key
|
|
36
48
|
const index = this.db.index('keyCount');
|
|
37
49
|
const rangeQuery = IDBKeyRange.bound([
|
|
38
50
|
this.container,
|
|
@@ -48,6 +60,8 @@ import { IndexedDBAztecMap } from './map.js';
|
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
async deleteValue(key, val) {
|
|
63
|
+
// Since we know the value, we can hash it and directly query the "hash" index
|
|
64
|
+
// to avoid having to iterate over all the values
|
|
51
65
|
const fullKey = await this.db.index('hash').getKey(IDBKeyRange.bound([
|
|
52
66
|
this.container,
|
|
53
67
|
this.normalizeKey(key),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/indexeddb/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAoB,MAAM,KAAK,CAAC;AAEzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAOhE,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,KAAK,IAAI;IACxC,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,cAAe,SAAQ,QAAQ;IAC9C,IAAI,EAAE;QACJ,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QACvB,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7E,CAAC;CACH;AAED;;GAEG;AAEH,qBAAa,mBAAoB,YAAW,iBAAiB;;aAaO,WAAW,EAAE,OAAO;gBAA1E,MAAM,EAAE,YAAY,CAAC,cAAc,CAAC,EAAkB,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAKjH;;;;;;;;;OASG;WACU,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,GAAE,OAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/indexeddb/store.ts"],"names":[],"mappings":"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"}
|
package/dest/indexeddb/store.js
CHANGED
|
@@ -42,6 +42,8 @@ import { IndexedDBAztecSingleton } from './singleton.js';
|
|
|
42
42
|
], {
|
|
43
43
|
unique: false
|
|
44
44
|
});
|
|
45
|
+
// Keep count of the maximum number of keys ever inserted in the container
|
|
46
|
+
// This allows unique slots for repeated keys, which is useful for multi-maps
|
|
45
47
|
objectStore.createIndex('keyCount', [
|
|
46
48
|
'container',
|
|
47
49
|
'key',
|
|
@@ -49,6 +51,8 @@ import { IndexedDBAztecSingleton } from './singleton.js';
|
|
|
49
51
|
], {
|
|
50
52
|
unique: false
|
|
51
53
|
});
|
|
54
|
+
// Keep an index on the pair key-hash for a given container, allowing us to efficiently
|
|
55
|
+
// delete unique values from multi-maps
|
|
52
56
|
objectStore.createIndex('hash', [
|
|
53
57
|
'container',
|
|
54
58
|
'key',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi_map_test_suite.d.ts","sourceRoot":"","sources":["../../src/interfaces/multi_map_test_suite.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGlE,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,EACzD,UAAU,GAAE,OAAe,
|
|
1
|
+
{"version":3,"file":"multi_map_test_suite.d.ts","sourceRoot":"","sources":["../../src/interfaces/multi_map_test_suite.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGlE,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,EACzD,UAAU,GAAE,OAAe,QA8J5B"}
|
|
@@ -95,11 +95,38 @@ export function describeAztecMultiMap(testName, getStore, forceAsync = false) {
|
|
|
95
95
|
]);
|
|
96
96
|
});
|
|
97
97
|
it('should be able to delete individual values for a single key', async ()=>{
|
|
98
|
+
await multiMap.set('foo', '1');
|
|
99
|
+
await multiMap.set('foo', '2');
|
|
100
|
+
await multiMap.set('foo', '3');
|
|
101
|
+
// Out-of-order delete
|
|
102
|
+
await multiMap.deleteValue('foo', '2');
|
|
103
|
+
expect(await getValues('foo')).to.deep.equal([
|
|
104
|
+
'1',
|
|
105
|
+
'3'
|
|
106
|
+
]);
|
|
107
|
+
// Insertion after delete
|
|
98
108
|
await multiMap.set('foo', 'bar');
|
|
99
|
-
await
|
|
109
|
+
expect(await getValues('foo')).to.deep.equal([
|
|
110
|
+
'1',
|
|
111
|
+
'3',
|
|
112
|
+
'bar'
|
|
113
|
+
]);
|
|
114
|
+
// Delete the last key
|
|
100
115
|
await multiMap.deleteValue('foo', 'bar');
|
|
101
116
|
expect(await getValues('foo')).to.deep.equal([
|
|
102
|
-
'
|
|
117
|
+
'1',
|
|
118
|
+
'3'
|
|
119
|
+
]);
|
|
120
|
+
// Reinsert the initially deleted key
|
|
121
|
+
await multiMap.set('foo', '2');
|
|
122
|
+
// LMDB and IndexedDB behave differently here, the former ordering by value and the
|
|
123
|
+
// latter by insertion. This is fine because there is no expectation for values in a map (or multimap) to
|
|
124
|
+
// be ordered.
|
|
125
|
+
const values = (await getValues('foo')).sort((a, b)=>a.localeCompare(b));
|
|
126
|
+
expect(values).to.deep.equal([
|
|
127
|
+
'1',
|
|
128
|
+
'2',
|
|
129
|
+
'3'
|
|
103
130
|
]);
|
|
104
131
|
});
|
|
105
132
|
it('supports range queries', async ()=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/kv-store",
|
|
3
|
-
"version": "0.85.0-alpha-testnet.
|
|
3
|
+
"version": "0.85.0-alpha-testnet.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/interfaces/index.js",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"./package.local.json"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aztec/ethereum": "0.85.0-alpha-testnet.
|
|
29
|
-
"@aztec/foundation": "0.85.0-alpha-testnet.
|
|
30
|
-
"@aztec/native": "0.85.0-alpha-testnet.
|
|
31
|
-
"@aztec/stdlib": "0.85.0-alpha-testnet.
|
|
28
|
+
"@aztec/ethereum": "0.85.0-alpha-testnet.7",
|
|
29
|
+
"@aztec/foundation": "0.85.0-alpha-testnet.7",
|
|
30
|
+
"@aztec/native": "0.85.0-alpha-testnet.7",
|
|
31
|
+
"@aztec/stdlib": "0.85.0-alpha-testnet.7",
|
|
32
32
|
"idb": "^8.0.0",
|
|
33
33
|
"lmdb": "^3.2.0",
|
|
34
34
|
"msgpackr": "^1.11.2",
|
|
@@ -12,6 +12,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
12
12
|
implements AztecAsyncMultiMap<K, V>
|
|
13
13
|
{
|
|
14
14
|
override async set(key: K, val: V): Promise<void> {
|
|
15
|
+
// Inserting repeated values is a no-op
|
|
15
16
|
const exists = !!(await this.db
|
|
16
17
|
.index('hash')
|
|
17
18
|
.get(
|
|
@@ -23,9 +24,19 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
23
24
|
if (exists) {
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// Get the maximum keyCount for the given key
|
|
28
|
+
// In order to support sparse multimaps, we cannot rely
|
|
29
|
+
// on just counting the number of entries for the key, since we would repeat slots
|
|
30
|
+
// if we delete an entry
|
|
31
|
+
// set -> container:key:0 (keyCount = 1)
|
|
32
|
+
// set -> container:key:1 (keyCount = 2)
|
|
33
|
+
// delete -> container:key:0 (keyCount = 1)
|
|
34
|
+
// set -> container:key:1 <--- already exists!
|
|
35
|
+
// Instead, we iterate in reverse order to get the last inserted entry
|
|
36
|
+
const index = this.db.index('keyCount');
|
|
37
|
+
const rangeQuery = IDBKeyRange.upperBound([this.container, this.normalizeKey(key), Number.MAX_SAFE_INTEGER]);
|
|
38
|
+
const maxEntry = (await index.iterate(rangeQuery, 'prevunique').next()).value;
|
|
39
|
+
const count = maxEntry?.value?.keyCount ?? 0;
|
|
29
40
|
await this.db.put({
|
|
30
41
|
value: val,
|
|
31
42
|
hash: hash(val),
|
|
@@ -37,6 +48,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
async *getValuesAsync(key: K): AsyncIterableIterator<V> {
|
|
51
|
+
// Iterate over the whole range of keyCount for the given key
|
|
40
52
|
const index = this.db.index('keyCount');
|
|
41
53
|
const rangeQuery = IDBKeyRange.bound(
|
|
42
54
|
[this.container, this.normalizeKey(key), 0],
|
|
@@ -50,6 +62,8 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
async deleteValue(key: K, val: V): Promise<void> {
|
|
65
|
+
// Since we know the value, we can hash it and directly query the "hash" index
|
|
66
|
+
// to avoid having to iterate over all the values
|
|
53
67
|
const fullKey = await this.db
|
|
54
68
|
.index('hash')
|
|
55
69
|
.getKey(
|
package/src/indexeddb/store.ts
CHANGED
|
@@ -73,7 +73,11 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
73
73
|
const objectStore = db.createObjectStore('data', { keyPath: 'slot' });
|
|
74
74
|
|
|
75
75
|
objectStore.createIndex('key', ['container', 'key'], { unique: false });
|
|
76
|
+
// Keep count of the maximum number of keys ever inserted in the container
|
|
77
|
+
// This allows unique slots for repeated keys, which is useful for multi-maps
|
|
76
78
|
objectStore.createIndex('keyCount', ['container', 'key', 'keyCount'], { unique: false });
|
|
79
|
+
// Keep an index on the pair key-hash for a given container, allowing us to efficiently
|
|
80
|
+
// delete unique values from multi-maps
|
|
77
81
|
objectStore.createIndex('hash', ['container', 'key', 'hash'], { unique: true });
|
|
78
82
|
},
|
|
79
83
|
});
|
|
@@ -124,12 +124,33 @@ export function describeAztecMultiMap(
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it('should be able to delete individual values for a single key', async () => {
|
|
127
|
+
await multiMap.set('foo', '1');
|
|
128
|
+
await multiMap.set('foo', '2');
|
|
129
|
+
await multiMap.set('foo', '3');
|
|
130
|
+
|
|
131
|
+
// Out-of-order delete
|
|
132
|
+
await multiMap.deleteValue('foo', '2');
|
|
133
|
+
|
|
134
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3']);
|
|
135
|
+
|
|
136
|
+
// Insertion after delete
|
|
127
137
|
await multiMap.set('foo', 'bar');
|
|
128
|
-
await multiMap.set('foo', 'baz');
|
|
129
138
|
|
|
139
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3', 'bar']);
|
|
140
|
+
|
|
141
|
+
// Delete the last key
|
|
130
142
|
await multiMap.deleteValue('foo', 'bar');
|
|
131
143
|
|
|
132
|
-
expect(await getValues('foo')).to.deep.equal(['
|
|
144
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3']);
|
|
145
|
+
|
|
146
|
+
// Reinsert the initially deleted key
|
|
147
|
+
await multiMap.set('foo', '2');
|
|
148
|
+
|
|
149
|
+
// LMDB and IndexedDB behave differently here, the former ordering by value and the
|
|
150
|
+
// latter by insertion. This is fine because there is no expectation for values in a map (or multimap) to
|
|
151
|
+
// be ordered.
|
|
152
|
+
const values = (await getValues('foo')).sort((a, b) => a.localeCompare(b));
|
|
153
|
+
expect(values).to.deep.equal(['1', '2', '3']);
|
|
133
154
|
});
|
|
134
155
|
|
|
135
156
|
it('supports range queries', async () => {
|