@aztec/kv-store 0.0.0-test.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.
Files changed (166) hide show
  1. package/README.md +10 -0
  2. package/dest/config.d.ts +17 -0
  3. package/dest/config.d.ts.map +1 -0
  4. package/dest/config.js +24 -0
  5. package/dest/indexeddb/array.d.ts +21 -0
  6. package/dest/indexeddb/array.d.ts.map +1 -0
  7. package/dest/indexeddb/array.js +104 -0
  8. package/dest/indexeddb/index.d.ts +7 -0
  9. package/dest/indexeddb/index.d.ts.map +1 -0
  10. package/dest/indexeddb/index.js +19 -0
  11. package/dest/indexeddb/map.d.ts +26 -0
  12. package/dest/indexeddb/map.d.ts.map +1 -0
  13. package/dest/indexeddb/map.js +133 -0
  14. package/dest/indexeddb/set.d.ts +17 -0
  15. package/dest/indexeddb/set.d.ts.map +1 -0
  16. package/dest/indexeddb/set.js +24 -0
  17. package/dest/indexeddb/singleton.d.ts +16 -0
  18. package/dest/indexeddb/singleton.d.ts.map +1 -0
  19. package/dest/indexeddb/singleton.js +37 -0
  20. package/dest/indexeddb/store.d.ts +97 -0
  21. package/dest/indexeddb/store.d.ts.map +1 -0
  22. package/dest/indexeddb/store.js +164 -0
  23. package/dest/interfaces/array.d.ts +80 -0
  24. package/dest/interfaces/array.d.ts.map +1 -0
  25. package/dest/interfaces/array.js +3 -0
  26. package/dest/interfaces/array_test_suite.d.ts +3 -0
  27. package/dest/interfaces/array_test_suite.d.ts.map +1 -0
  28. package/dest/interfaces/array_test_suite.js +104 -0
  29. package/dest/interfaces/common.d.ts +23 -0
  30. package/dest/interfaces/common.d.ts.map +1 -0
  31. package/dest/interfaces/common.js +3 -0
  32. package/dest/interfaces/counter.d.ts +59 -0
  33. package/dest/interfaces/counter.d.ts.map +1 -0
  34. package/dest/interfaces/counter.js +1 -0
  35. package/dest/interfaces/index.d.ts +8 -0
  36. package/dest/interfaces/index.d.ts.map +1 -0
  37. package/dest/interfaces/index.js +6 -0
  38. package/dest/interfaces/map.d.ts +134 -0
  39. package/dest/interfaces/map.d.ts.map +1 -0
  40. package/dest/interfaces/map.js +3 -0
  41. package/dest/interfaces/map_test_suite.d.ts +3 -0
  42. package/dest/interfaces/map_test_suite.d.ts.map +1 -0
  43. package/dest/interfaces/map_test_suite.js +151 -0
  44. package/dest/interfaces/set.d.ts +44 -0
  45. package/dest/interfaces/set.d.ts.map +1 -0
  46. package/dest/interfaces/set.js +1 -0
  47. package/dest/interfaces/set_test_suite.d.ts +3 -0
  48. package/dest/interfaces/set_test_suite.d.ts.map +1 -0
  49. package/dest/interfaces/set_test_suite.js +96 -0
  50. package/dest/interfaces/singleton.d.ts +29 -0
  51. package/dest/interfaces/singleton.d.ts.map +1 -0
  52. package/dest/interfaces/singleton.js +4 -0
  53. package/dest/interfaces/singleton_test_suite.d.ts +3 -0
  54. package/dest/interfaces/singleton_test_suite.d.ts.map +1 -0
  55. package/dest/interfaces/singleton_test_suite.js +30 -0
  56. package/dest/interfaces/store.d.ts +145 -0
  57. package/dest/interfaces/store.d.ts.map +1 -0
  58. package/dest/interfaces/store.js +1 -0
  59. package/dest/interfaces/store_test_suite.d.ts +3 -0
  60. package/dest/interfaces/store_test_suite.d.ts.map +1 -0
  61. package/dest/interfaces/store_test_suite.js +37 -0
  62. package/dest/interfaces/utils.d.ts +16 -0
  63. package/dest/interfaces/utils.d.ts.map +1 -0
  64. package/dest/interfaces/utils.js +16 -0
  65. package/dest/lmdb/array.d.ts +23 -0
  66. package/dest/lmdb/array.d.ts.map +1 -0
  67. package/dest/lmdb/array.js +117 -0
  68. package/dest/lmdb/counter.d.ts +19 -0
  69. package/dest/lmdb/counter.d.ts.map +1 -0
  70. package/dest/lmdb/counter.js +50 -0
  71. package/dest/lmdb/index.d.ts +12 -0
  72. package/dest/lmdb/index.d.ts.map +1 -0
  73. package/dest/lmdb/index.js +25 -0
  74. package/dest/lmdb/map.d.ts +52 -0
  75. package/dest/lmdb/map.d.ts.map +1 -0
  76. package/dest/lmdb/map.js +222 -0
  77. package/dest/lmdb/set.d.ts +18 -0
  78. package/dest/lmdb/set.d.ts.map +1 -0
  79. package/dest/lmdb/set.js +32 -0
  80. package/dest/lmdb/singleton.d.ts +14 -0
  81. package/dest/lmdb/singleton.d.ts.map +1 -0
  82. package/dest/lmdb/singleton.js +26 -0
  83. package/dest/lmdb/store.d.ts +110 -0
  84. package/dest/lmdb/store.d.ts.map +1 -0
  85. package/dest/lmdb/store.js +216 -0
  86. package/dest/lmdb-v2/array.d.ts +18 -0
  87. package/dest/lmdb-v2/array.d.ts.map +1 -0
  88. package/dest/lmdb-v2/array.js +101 -0
  89. package/dest/lmdb-v2/factory.d.ts +12 -0
  90. package/dest/lmdb-v2/factory.d.ts.map +1 -0
  91. package/dest/lmdb-v2/factory.js +60 -0
  92. package/dest/lmdb-v2/index.d.ts +3 -0
  93. package/dest/lmdb-v2/index.d.ts.map +1 -0
  94. package/dest/lmdb-v2/index.js +2 -0
  95. package/dest/lmdb-v2/map.d.ts +86 -0
  96. package/dest/lmdb-v2/map.d.ts.map +1 -0
  97. package/dest/lmdb-v2/map.js +193 -0
  98. package/dest/lmdb-v2/message.d.ts +112 -0
  99. package/dest/lmdb-v2/message.d.ts.map +1 -0
  100. package/dest/lmdb-v2/message.js +18 -0
  101. package/dest/lmdb-v2/read_transaction.d.ts +14 -0
  102. package/dest/lmdb-v2/read_transaction.d.ts.map +1 -0
  103. package/dest/lmdb-v2/read_transaction.js +101 -0
  104. package/dest/lmdb-v2/singleton.d.ts +12 -0
  105. package/dest/lmdb-v2/singleton.d.ts.map +1 -0
  106. package/dest/lmdb-v2/singleton.js +31 -0
  107. package/dest/lmdb-v2/store.d.ts +43 -0
  108. package/dest/lmdb-v2/store.d.ts.map +1 -0
  109. package/dest/lmdb-v2/store.js +181 -0
  110. package/dest/lmdb-v2/utils.d.ts +19 -0
  111. package/dest/lmdb-v2/utils.d.ts.map +1 -0
  112. package/dest/lmdb-v2/utils.js +126 -0
  113. package/dest/lmdb-v2/write_transaction.d.ts +19 -0
  114. package/dest/lmdb-v2/write_transaction.d.ts.map +1 -0
  115. package/dest/lmdb-v2/write_transaction.js +254 -0
  116. package/dest/stores/index.d.ts +2 -0
  117. package/dest/stores/index.d.ts.map +1 -0
  118. package/dest/stores/index.js +1 -0
  119. package/dest/stores/l2_tips_store.d.ts +13 -0
  120. package/dest/stores/l2_tips_store.d.ts.map +1 -0
  121. package/dest/stores/l2_tips_store.js +65 -0
  122. package/dest/utils.d.ts +12 -0
  123. package/dest/utils.d.ts.map +1 -0
  124. package/dest/utils.js +24 -0
  125. package/package.json +99 -0
  126. package/src/config.ts +34 -0
  127. package/src/indexeddb/array.ts +118 -0
  128. package/src/indexeddb/index.ts +29 -0
  129. package/src/indexeddb/map.ts +142 -0
  130. package/src/indexeddb/set.ts +37 -0
  131. package/src/indexeddb/singleton.ts +49 -0
  132. package/src/indexeddb/store.ts +197 -0
  133. package/src/interfaces/array.ts +90 -0
  134. package/src/interfaces/array_test_suite.ts +130 -0
  135. package/src/interfaces/common.ts +20 -0
  136. package/src/interfaces/counter.ts +65 -0
  137. package/src/interfaces/index.ts +7 -0
  138. package/src/interfaces/map.ts +151 -0
  139. package/src/interfaces/map_test_suite.ts +143 -0
  140. package/src/interfaces/set.ts +48 -0
  141. package/src/interfaces/set_test_suite.ts +81 -0
  142. package/src/interfaces/singleton.ts +29 -0
  143. package/src/interfaces/singleton_test_suite.ts +46 -0
  144. package/src/interfaces/store.ts +177 -0
  145. package/src/interfaces/store_test_suite.ts +56 -0
  146. package/src/interfaces/utils.ts +21 -0
  147. package/src/lmdb/array.ts +139 -0
  148. package/src/lmdb/counter.ts +67 -0
  149. package/src/lmdb/index.ts +37 -0
  150. package/src/lmdb/map.ts +248 -0
  151. package/src/lmdb/set.ts +45 -0
  152. package/src/lmdb/singleton.ts +35 -0
  153. package/src/lmdb/store.ts +263 -0
  154. package/src/lmdb-v2/array.ts +115 -0
  155. package/src/lmdb-v2/factory.ts +98 -0
  156. package/src/lmdb-v2/index.ts +2 -0
  157. package/src/lmdb-v2/map.ts +233 -0
  158. package/src/lmdb-v2/message.ts +146 -0
  159. package/src/lmdb-v2/read_transaction.ts +116 -0
  160. package/src/lmdb-v2/singleton.ts +34 -0
  161. package/src/lmdb-v2/store.ts +237 -0
  162. package/src/lmdb-v2/utils.ts +150 -0
  163. package/src/lmdb-v2/write_transaction.ts +314 -0
  164. package/src/stores/index.ts +1 -0
  165. package/src/stores/l2_tips_store.ts +70 -0
  166. package/src/utils.ts +40 -0
@@ -0,0 +1,181 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import { Semaphore, SerialQueue } from '@aztec/foundation/queue';
3
+ import { MsgpackChannel, NativeLMDBStore } from '@aztec/native';
4
+ import { AsyncLocalStorage } from 'async_hooks';
5
+ import { rm } from 'fs/promises';
6
+ import { LMDBArray } from './array.js';
7
+ import { LMDBMap, LMDBMultiMap } from './map.js';
8
+ import { Database, LMDBMessageType } from './message.js';
9
+ import { ReadTransaction } from './read_transaction.js';
10
+ import { LMDBSingleValue } from './singleton.js';
11
+ import { WriteTransaction } from './write_transaction.js';
12
+ export class AztecLMDBStoreV2 {
13
+ dataDir;
14
+ log;
15
+ cleanup;
16
+ open;
17
+ channel;
18
+ writerCtx;
19
+ writerQueue;
20
+ availableCursors;
21
+ constructor(dataDir, mapSize, maxReaders, log, cleanup){
22
+ this.dataDir = dataDir;
23
+ this.log = log;
24
+ this.cleanup = cleanup;
25
+ this.open = false;
26
+ this.writerCtx = new AsyncLocalStorage();
27
+ this.writerQueue = new SerialQueue();
28
+ this.log.info(`Starting data store with maxReaders ${maxReaders}`);
29
+ this.channel = new MsgpackChannel(new NativeLMDBStore(dataDir, mapSize, maxReaders));
30
+ // leave one reader to always be available for regular, atomic, reads
31
+ this.availableCursors = new Semaphore(maxReaders - 1);
32
+ }
33
+ get dataDirectory() {
34
+ return this.dataDir;
35
+ }
36
+ async start() {
37
+ this.writerQueue.start();
38
+ await this.channel.sendMessage(LMDBMessageType.OPEN_DATABASE, {
39
+ db: Database.DATA,
40
+ uniqueKeys: true
41
+ });
42
+ await this.channel.sendMessage(LMDBMessageType.OPEN_DATABASE, {
43
+ db: Database.INDEX,
44
+ uniqueKeys: false
45
+ });
46
+ this.open = true;
47
+ }
48
+ static async new(dataDir, dbMapSizeKb = 10 * 1024 * 1024, maxReaders = 16, cleanup, log = createLogger('kv-store:lmdb-v2')) {
49
+ const db = new AztecLMDBStoreV2(dataDir, dbMapSizeKb, maxReaders, log, cleanup);
50
+ await db.start();
51
+ return db;
52
+ }
53
+ getReadTx() {
54
+ if (!this.open) {
55
+ throw new Error('Store is closed');
56
+ }
57
+ return new ReadTransaction(this);
58
+ }
59
+ getCurrentWriteTx() {
60
+ if (!this.open) {
61
+ throw new Error('Store is closed');
62
+ }
63
+ const currentWrite = this.writerCtx.getStore();
64
+ return currentWrite;
65
+ }
66
+ openMap(name) {
67
+ return new LMDBMap(this, name);
68
+ }
69
+ openMultiMap(name) {
70
+ return new LMDBMultiMap(this, name);
71
+ }
72
+ openSingleton(name) {
73
+ return new LMDBSingleValue(this, name);
74
+ }
75
+ openArray(name) {
76
+ return new LMDBArray(this, name);
77
+ }
78
+ openSet(_name) {
79
+ throw new Error('Not implemented');
80
+ }
81
+ openCounter(_name) {
82
+ throw new Error('Not implemented');
83
+ }
84
+ async transactionAsync(callback) {
85
+ if (!this.open) {
86
+ throw new Error('Store is closed');
87
+ }
88
+ // transactionAsync might be called recursively
89
+ // send any writes to the parent tx, but don't close it
90
+ // if the callback throws then the parent tx will rollback automatically
91
+ const currentTx = this.getCurrentWriteTx();
92
+ if (currentTx) {
93
+ return await callback(currentTx);
94
+ }
95
+ return this.writerQueue.put(async ()=>{
96
+ const tx = new WriteTransaction(this);
97
+ try {
98
+ const res = await this.writerCtx.run(tx, callback, tx);
99
+ await tx.commit();
100
+ return res;
101
+ } catch (err) {
102
+ this.log.error(`Failed to commit transaction`, err);
103
+ throw err;
104
+ } finally{
105
+ tx.close();
106
+ }
107
+ });
108
+ }
109
+ clear() {
110
+ return Promise.resolve();
111
+ }
112
+ fork() {
113
+ throw new Error('Not implemented');
114
+ }
115
+ async delete() {
116
+ await this.close();
117
+ await rm(this.dataDir, {
118
+ recursive: true,
119
+ force: true,
120
+ maxRetries: 3
121
+ });
122
+ this.log.verbose(`Deleted database files at ${this.dataDir}`);
123
+ await this.cleanup?.();
124
+ }
125
+ async close() {
126
+ if (!this.open) {
127
+ // already closed
128
+ return;
129
+ }
130
+ this.open = false;
131
+ await this.writerQueue.cancel();
132
+ await this.channel.sendMessage(LMDBMessageType.CLOSE, undefined);
133
+ }
134
+ async sendMessage(msgType, body) {
135
+ if (!this.open) {
136
+ throw new Error('Store is closed');
137
+ }
138
+ if (msgType === LMDBMessageType.START_CURSOR) {
139
+ await this.availableCursors.acquire();
140
+ }
141
+ let response = undefined;
142
+ try {
143
+ ({ response } = await this.channel.sendMessage(msgType, body));
144
+ return response;
145
+ } finally{
146
+ if (msgType === LMDBMessageType.START_CURSOR && response === undefined || msgType === LMDBMessageType.CLOSE_CURSOR || // it's possible for a START_CURSOR command to not return a cursor (e.g. db is empty)
147
+ msgType === LMDBMessageType.START_CURSOR && typeof response.cursor !== 'number') {
148
+ this.availableCursors.release();
149
+ }
150
+ }
151
+ }
152
+ async estimateSize() {
153
+ const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
154
+ return {
155
+ mappingSize: Number(resp.dbMapSizeBytes),
156
+ actualSize: resp.stats.reduce((s, db)=>Number(db.totalUsedSize) + s, 0),
157
+ numItems: resp.stats.reduce((s, db)=>Number(db.numDataItems) + s, 0)
158
+ };
159
+ }
160
+ }
161
+ export function execInWriteTx(store, fn) {
162
+ const currentWrite = store.getCurrentWriteTx();
163
+ if (currentWrite) {
164
+ return fn(currentWrite);
165
+ } else {
166
+ return store.transactionAsync(fn);
167
+ }
168
+ }
169
+ export async function execInReadTx(store, fn) {
170
+ const currentWrite = store.getCurrentWriteTx();
171
+ if (currentWrite) {
172
+ return await fn(currentWrite);
173
+ } else {
174
+ const tx = store.getReadTx();
175
+ try {
176
+ return await fn(tx);
177
+ } finally{
178
+ tx.close();
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,19 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import type { Key } from '../interfaces/common.js';
4
+ type Cmp<T> = (a: T, b: T) => -1 | 0 | 1;
5
+ export declare function dedupeSortedArray<T>(arr: T[], cmp: Cmp<T>): void;
6
+ export declare function insertIntoSortedArray<T>(arr: T[], item: T, cmp: (a: T, b: T) => number): void;
7
+ export declare function findIndexInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): number;
8
+ export declare function findInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): T | undefined;
9
+ export declare function removeAnyOf<T, N>(arr: T[], vals: N[], cmp: (a: T, b: N) => -1 | 0 | 1): void;
10
+ export declare function removeFromSortedArray<T, N>(arr: T[], val: N, cmp: (a: T, b: N) => -1 | 0 | 1): void;
11
+ export declare function merge<T>(arr: T[], toInsert: T[], cmp: (a: T, b: T) => -1 | 0 | 1): void;
12
+ export declare function keyCmp(a: [Uint8Array, Uint8Array[] | null], b: [Uint8Array, Uint8Array[] | null]): -1 | 0 | 1;
13
+ export declare function singleKeyCmp(a: [Uint8Array, Uint8Array[] | null], b: Uint8Array): -1 | 0 | 1;
14
+ export declare function minKey(prefix: string): Buffer;
15
+ export declare function maxKey(prefix: string): Buffer;
16
+ export declare function serializeKey(prefix: string, key: Key): Buffer;
17
+ export declare function deserializeKey<K extends Key>(prefix: string, key: Uint8Array): K | false;
18
+ export {};
19
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/utils.ts"],"names":[],"mappings":";;AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEzC,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAkBhE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAiBxG;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,SAAS,CAG1G;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAsB5F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAK5F;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAqBvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE7G;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE5F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,UAEpC;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,UAEpC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAE7D;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,CAAC,GAAG,KAAK,CAOxF"}
@@ -0,0 +1,126 @@
1
+ import { MAXIMUM_KEY, fromBufferKey, toBufferKey } from 'ordered-binary';
2
+ export function dedupeSortedArray(arr, cmp) {
3
+ for(let i = 0; i < arr.length; i++){
4
+ let j = i + 1;
5
+ for(; j < arr.length; j++){
6
+ const res = cmp(arr[i], arr[j]);
7
+ if (res === 0) {
8
+ continue;
9
+ } else if (res < 0) {
10
+ break;
11
+ } else {
12
+ throw new Error('Array not sorted');
13
+ }
14
+ }
15
+ if (j - i > 1) {
16
+ arr.splice(i + 1, j - i - 1);
17
+ }
18
+ }
19
+ }
20
+ export function insertIntoSortedArray(arr, item, cmp) {
21
+ let left = 0;
22
+ let right = arr.length;
23
+ while(left < right){
24
+ const mid = left + right >> 1;
25
+ const comparison = cmp(arr[mid], item);
26
+ if (comparison < 0) {
27
+ left = mid + 1;
28
+ } else {
29
+ right = mid;
30
+ }
31
+ }
32
+ arr.splice(left, 0, item);
33
+ }
34
+ export function findIndexInSortedArray(values, needle, cmp) {
35
+ let start = 0;
36
+ let end = values.length - 1;
37
+ while(start <= end){
38
+ const mid = start + ((end - start) / 2 | 0);
39
+ const res = cmp(values[mid], needle);
40
+ if (res === 0) {
41
+ return mid;
42
+ } else if (res > 0) {
43
+ end = mid - 1;
44
+ } else {
45
+ start = mid + 1;
46
+ }
47
+ }
48
+ return -1;
49
+ }
50
+ export function findInSortedArray(values, needle, cmp) {
51
+ const idx = findIndexInSortedArray(values, needle, cmp);
52
+ return idx > -1 ? values[idx] : undefined;
53
+ }
54
+ export function removeAnyOf(arr, vals, cmp) {
55
+ let writeIdx = 0;
56
+ let readIdx = 0;
57
+ let valIdx = 0;
58
+ while(readIdx < arr.length && valIdx < vals.length){
59
+ const comparison = cmp(arr[readIdx], vals[valIdx]);
60
+ if (comparison < 0) {
61
+ arr[writeIdx++] = arr[readIdx++];
62
+ } else if (comparison > 0) {
63
+ valIdx++;
64
+ } else {
65
+ readIdx++;
66
+ }
67
+ }
68
+ while(readIdx < arr.length){
69
+ arr[writeIdx++] = arr[readIdx++];
70
+ }
71
+ arr.length = writeIdx;
72
+ }
73
+ export function removeFromSortedArray(arr, val, cmp) {
74
+ const idx = findIndexInSortedArray(arr, val, cmp);
75
+ if (idx > -1) {
76
+ arr.splice(idx, 1);
77
+ }
78
+ }
79
+ export function merge(arr, toInsert, cmp) {
80
+ const result = new Array(arr.length + toInsert.length);
81
+ let i = 0, j = 0, k = 0;
82
+ while(i < arr.length && j < toInsert.length){
83
+ result[k++] = cmp(arr[i], toInsert[j]) <= 0 ? arr[i++] : toInsert[j++];
84
+ }
85
+ while(i < arr.length){
86
+ result[k++] = arr[i++];
87
+ }
88
+ while(j < toInsert.length){
89
+ result[k++] = toInsert[j++];
90
+ }
91
+ for(i = 0; i < result.length; i++){
92
+ arr[i] = result[i];
93
+ }
94
+ arr.length = result.length;
95
+ }
96
+ export function keyCmp(a, b) {
97
+ return Buffer.compare(a[0], b[0]);
98
+ }
99
+ export function singleKeyCmp(a, b) {
100
+ return Buffer.compare(a[0], b);
101
+ }
102
+ export function minKey(prefix) {
103
+ return toBufferKey([
104
+ prefix
105
+ ]);
106
+ }
107
+ export function maxKey(prefix) {
108
+ return toBufferKey([
109
+ prefix,
110
+ MAXIMUM_KEY
111
+ ]);
112
+ }
113
+ export function serializeKey(prefix, key) {
114
+ return toBufferKey([
115
+ prefix,
116
+ key
117
+ ]);
118
+ }
119
+ export function deserializeKey(prefix, key) {
120
+ const buf = Buffer.from(key);
121
+ const parsed = fromBufferKey(buf);
122
+ if (!Array.isArray(parsed) || parsed[0] !== prefix) {
123
+ return false;
124
+ }
125
+ return parsed[1];
126
+ }
@@ -0,0 +1,19 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { type Batch } from './message.js';
4
+ import { ReadTransaction } from './read_transaction.js';
5
+ export declare class WriteTransaction extends ReadTransaction {
6
+ #private;
7
+ readonly dataBatch: Batch;
8
+ readonly indexBatch: Batch;
9
+ set(key: Uint8Array, value: Uint8Array): Promise<void>;
10
+ remove(key: Uint8Array): Promise<void>;
11
+ get(key: Buffer): Promise<Uint8Array | undefined>;
12
+ setIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
13
+ removeIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
14
+ getIndex(key: Buffer): Promise<Uint8Array[]>;
15
+ iterate(startKey: Uint8Array, endKey?: Uint8Array | undefined, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array]>;
16
+ iterateIndex(startKey: Uint8Array, endKey?: Uint8Array | undefined, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array[]]>;
17
+ commit(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=write_transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAchB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAevE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzD,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB3C,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,EAC/B,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAanB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,EAC/B,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAqH/B,MAAM;CAUpB"}
@@ -0,0 +1,254 @@
1
+ import { Database, LMDBMessageType } from './message.js';
2
+ import { ReadTransaction } from './read_transaction.js';
3
+ import { dedupeSortedArray, findInSortedArray, findIndexInSortedArray, insertIntoSortedArray, keyCmp, merge, removeAnyOf, removeFromSortedArray, singleKeyCmp } from './utils.js';
4
+ export class WriteTransaction extends ReadTransaction {
5
+ // exposed for tests
6
+ dataBatch = {
7
+ addEntries: [],
8
+ removeEntries: []
9
+ };
10
+ indexBatch = {
11
+ addEntries: [],
12
+ removeEntries: []
13
+ };
14
+ set(key, value) {
15
+ this.assertIsOpen();
16
+ const addEntry = findInSortedArray(this.dataBatch.addEntries, key, singleKeyCmp);
17
+ if (!addEntry) {
18
+ insertIntoSortedArray(this.dataBatch.addEntries, [
19
+ key,
20
+ [
21
+ value
22
+ ]
23
+ ], keyCmp);
24
+ } else {
25
+ addEntry[1] = [
26
+ value
27
+ ];
28
+ }
29
+ const removeEntryIndex = findIndexInSortedArray(this.dataBatch.removeEntries, key, singleKeyCmp);
30
+ if (removeEntryIndex > -1) {
31
+ this.dataBatch.removeEntries.splice(removeEntryIndex, 1);
32
+ }
33
+ return Promise.resolve();
34
+ }
35
+ remove(key) {
36
+ const removeEntryIndex = findIndexInSortedArray(this.dataBatch.removeEntries, key, singleKeyCmp);
37
+ if (removeEntryIndex === -1) {
38
+ this.dataBatch.removeEntries.push([
39
+ key,
40
+ null
41
+ ]);
42
+ }
43
+ const addEntryIndex = findIndexInSortedArray(this.dataBatch.addEntries, key, singleKeyCmp);
44
+ if (addEntryIndex > -1) {
45
+ this.dataBatch.addEntries.splice(addEntryIndex, 1);
46
+ }
47
+ return Promise.resolve();
48
+ }
49
+ async get(key) {
50
+ this.assertIsOpen();
51
+ const addEntry = findInSortedArray(this.dataBatch.addEntries, key, singleKeyCmp);
52
+ if (addEntry) {
53
+ return addEntry[1][0];
54
+ }
55
+ const removeEntryIdx = findIndexInSortedArray(this.dataBatch.removeEntries, key, singleKeyCmp);
56
+ if (removeEntryIdx > -1) {
57
+ return undefined;
58
+ }
59
+ return await super.get(key);
60
+ }
61
+ setIndex(key, ...values) {
62
+ this.assertIsOpen();
63
+ const addEntries = findInSortedArray(this.indexBatch.addEntries, key, singleKeyCmp);
64
+ const removeEntries = findInSortedArray(this.indexBatch.removeEntries, key, singleKeyCmp);
65
+ if (removeEntries) {
66
+ if (removeEntries[1]) {
67
+ // check if we were deleting these values and update
68
+ removeAnyOf(removeEntries[1], values, Buffer.compare);
69
+ }
70
+ if (!removeEntries[1] || removeEntries[1].length === 0) {
71
+ // either we were deleting the entire key previously
72
+ // or after cleaning up duplicates, we don't have anything else to delete
73
+ removeFromSortedArray(this.indexBatch.removeEntries, removeEntries, keyCmp);
74
+ }
75
+ }
76
+ if (addEntries) {
77
+ merge(addEntries[1], values, Buffer.compare);
78
+ dedupeSortedArray(addEntries[1], Buffer.compare);
79
+ } else {
80
+ insertIntoSortedArray(this.indexBatch.addEntries, [
81
+ key,
82
+ values
83
+ ], keyCmp);
84
+ }
85
+ return Promise.resolve();
86
+ }
87
+ removeIndex(key, ...values) {
88
+ this.assertIsOpen();
89
+ const addEntries = findInSortedArray(this.indexBatch.addEntries, key, singleKeyCmp);
90
+ const removeEntries = findInSortedArray(this.indexBatch.removeEntries, key, singleKeyCmp);
91
+ if (values.length === 0) {
92
+ // special case, we're deleting the entire key
93
+ if (addEntries) {
94
+ removeFromSortedArray(this.indexBatch.addEntries, addEntries, keyCmp);
95
+ }
96
+ if (removeEntries) {
97
+ removeEntries[1] = null;
98
+ } else {
99
+ insertIntoSortedArray(this.indexBatch.removeEntries, [
100
+ key,
101
+ null
102
+ ], keyCmp);
103
+ }
104
+ return Promise.resolve();
105
+ }
106
+ if (addEntries) {
107
+ removeAnyOf(addEntries[1], values, Buffer.compare);
108
+ if (addEntries[1].length === 0) {
109
+ removeFromSortedArray(this.indexBatch.addEntries, addEntries, keyCmp);
110
+ }
111
+ }
112
+ if (removeEntries) {
113
+ removeEntries[1] ??= [];
114
+ merge(removeEntries[1], values, Buffer.compare);
115
+ dedupeSortedArray(removeEntries[1], Buffer.compare);
116
+ } else {
117
+ insertIntoSortedArray(this.indexBatch.removeEntries, [
118
+ key,
119
+ values
120
+ ], keyCmp);
121
+ }
122
+ return Promise.resolve();
123
+ }
124
+ async getIndex(key) {
125
+ this.assertIsOpen();
126
+ const removeEntries = findInSortedArray(this.indexBatch.removeEntries, key, singleKeyCmp);
127
+ if (removeEntries && removeEntries[1] === null) {
128
+ return [];
129
+ }
130
+ const addEntries = findInSortedArray(this.indexBatch.addEntries, key, singleKeyCmp);
131
+ const results = await super.getIndex(key);
132
+ if (addEntries) {
133
+ merge(results, addEntries[1], Buffer.compare);
134
+ dedupeSortedArray(results, Buffer.compare);
135
+ }
136
+ if (removeEntries && Array.isArray(removeEntries[1])) {
137
+ removeAnyOf(results, removeEntries[1], Buffer.compare);
138
+ }
139
+ return results;
140
+ }
141
+ async *iterate(startKey, endKey, reverse, limit) {
142
+ yield* this.#iterate(super.iterate(startKey, endKey, reverse), this.dataBatch, startKey, endKey, reverse, limit, (committed, toAdd)=>toAdd.length > 0 ? toAdd[0] : committed, (vals)=>vals[0]);
143
+ }
144
+ async *iterateIndex(startKey, endKey, reverse, limit) {
145
+ yield* this.#iterate(super.iterateIndex(startKey, endKey, reverse), this.indexBatch, startKey, endKey, reverse, limit, (committed, toAdd, toRemove)=>{
146
+ if (toAdd.length > 0) {
147
+ merge(committed, toAdd, Buffer.compare);
148
+ dedupeSortedArray(committed, Buffer.compare);
149
+ }
150
+ if (toRemove.length > 0) {
151
+ removeAnyOf(committed, toRemove, Buffer.compare);
152
+ }
153
+ return committed;
154
+ }, (vals)=>vals);
155
+ }
156
+ async *#iterate(iterator, batch, startKey, endKey, reverse = false, limit, merge, map) {
157
+ this.assertIsOpen();
158
+ // make a copy of this in case we're running in reverse
159
+ const uncommittedEntries = [
160
+ ...batch.addEntries
161
+ ];
162
+ // used to check we're in the right order when comparing between a key and uncommittedEntries
163
+ let cmpDirection = -1;
164
+ if (reverse) {
165
+ cmpDirection = 1;
166
+ uncommittedEntries.reverse();
167
+ }
168
+ let uncommittedEntriesIdx = 0;
169
+ while(uncommittedEntriesIdx < uncommittedEntries.length){
170
+ const entry = uncommittedEntries[uncommittedEntriesIdx];
171
+ // go to the first key in our cache that would be captured by the iterator
172
+ if (Buffer.compare(entry[0], startKey) !== cmpDirection) {
173
+ break;
174
+ }
175
+ uncommittedEntriesIdx++;
176
+ }
177
+ let count = 0;
178
+ // helper to early return if we've reached our limit
179
+ const checkLimit = typeof limit === 'number' ? ()=>count < limit : ()=>true;
180
+ for await (const [key, values] of iterator){
181
+ // yield every key that we have cached that's captured by the iterator
182
+ while(uncommittedEntriesIdx < uncommittedEntries.length && checkLimit()){
183
+ const entry = uncommittedEntries[uncommittedEntriesIdx];
184
+ if (endKey && Buffer.compare(entry[0], endKey) !== cmpDirection) {
185
+ break;
186
+ }
187
+ if (Buffer.compare(entry[0], key) === cmpDirection) {
188
+ count++;
189
+ yield [
190
+ entry[0],
191
+ map(entry[1])
192
+ ];
193
+ } else {
194
+ break;
195
+ }
196
+ uncommittedEntriesIdx++;
197
+ }
198
+ if (!checkLimit()) {
199
+ break;
200
+ }
201
+ const toRemove = findInSortedArray(batch.removeEntries, key, singleKeyCmp);
202
+ // at this point we've either exhausted all uncommitted entries,
203
+ // we reached a key strictly greater/smaller than `key`
204
+ // or we found the key itself
205
+ // check if it's the key and use the uncommitted value
206
+ let toAdd = [];
207
+ if (uncommittedEntriesIdx < uncommittedEntries.length && Buffer.compare(uncommittedEntries[uncommittedEntriesIdx][0], key) === 0) {
208
+ toAdd = uncommittedEntries[uncommittedEntriesIdx][1];
209
+ uncommittedEntriesIdx++;
210
+ }
211
+ if (toRemove && !toRemove[1]) {
212
+ continue;
213
+ } else {
214
+ const mergedValues = merge(values, toAdd, toRemove?.[1] ?? []);
215
+ if (mergedValues) {
216
+ count++;
217
+ yield [
218
+ key,
219
+ mergedValues
220
+ ];
221
+ }
222
+ }
223
+ }
224
+ // emit all the uncommitted data that would be captured by this iterator
225
+ while(uncommittedEntriesIdx < uncommittedEntries.length && checkLimit()){
226
+ const entry = uncommittedEntries[uncommittedEntriesIdx];
227
+ if (endKey && Buffer.compare(entry[0], endKey) !== cmpDirection) {
228
+ break;
229
+ }
230
+ count++;
231
+ yield [
232
+ entry[0],
233
+ map(entry[1])
234
+ ];
235
+ uncommittedEntriesIdx++;
236
+ }
237
+ }
238
+ async commit() {
239
+ this.assertIsOpen();
240
+ this.close();
241
+ await this.channel.sendMessage(LMDBMessageType.BATCH, {
242
+ batches: new Map([
243
+ [
244
+ Database.DATA,
245
+ this.dataBatch
246
+ ],
247
+ [
248
+ Database.INDEX,
249
+ this.indexBatch
250
+ ]
251
+ ])
252
+ });
253
+ }
254
+ }
@@ -0,0 +1,2 @@
1
+ export * from './l2_tips_store.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './l2_tips_store.js';
@@ -0,0 +1,13 @@
1
+ import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider, L2Tips } from '@aztec/stdlib/block';
2
+ import type { AztecAsyncKVStore } from '../interfaces/store.js';
3
+ /** Stores currently synced L2 tips and unfinalized block hashes. */
4
+ export declare class L2TipsStore implements L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider {
5
+ private readonly l2TipsStore;
6
+ private readonly l2BlockHashesStore;
7
+ constructor(store: AztecAsyncKVStore, namespace: string);
8
+ getL2BlockHash(number: number): Promise<string | undefined>;
9
+ getL2Tips(): Promise<L2Tips>;
10
+ private getL2Tip;
11
+ handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=l2_tips_store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"l2_tips_store.d.ts","sourceRoot":"","sources":["../../src/stores/l2_tips_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,yBAAyB,EACzB,8BAA8B,EAE9B,MAAM,EACP,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,oEAAoE;AACpE,qBAAa,WAAY,YAAW,yBAAyB,EAAE,8BAA8B;IAC3F,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAChE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAgC;gBAEvD,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM;IAKhD,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIrD,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;YAQ3B,QAAQ;IAaT,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;CAsB9E"}