@aztec/kv-store 0.85.0-alpha-testnet.3 → 0.85.0-alpha-testnet.5

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.
@@ -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;IAyB1C,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC;IAajD,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAajD"}
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
- const count = await this.db.index('key').count(IDBKeyRange.bound([
20
- this.container,
21
- this.normalizeKey(key)
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":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/indexeddb/store.ts"],"names":[],"mappings":"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"}
@@ -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,QAyI5B"}
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 multiMap.set('foo', 'baz');
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
- 'baz'
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",
3
+ "version": "0.85.0-alpha-testnet.5",
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.3",
29
- "@aztec/foundation": "0.85.0-alpha-testnet.3",
30
- "@aztec/native": "0.85.0-alpha-testnet.3",
31
- "@aztec/stdlib": "0.85.0-alpha-testnet.3",
28
+ "@aztec/ethereum": "0.85.0-alpha-testnet.5",
29
+ "@aztec/foundation": "0.85.0-alpha-testnet.5",
30
+ "@aztec/native": "0.85.0-alpha-testnet.5",
31
+ "@aztec/stdlib": "0.85.0-alpha-testnet.5",
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
- const count = await this.db
27
- .index('key')
28
- .count(IDBKeyRange.bound([this.container, this.normalizeKey(key)], [this.container, this.normalizeKey(key)]));
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(
@@ -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(['baz']);
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 () => {