@aztec/world-state 0.0.1-commit.343b43af6 → 0.0.1-commit.350e0a4d
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/instrumentation/instrumentation.d.ts +4 -3
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +4 -9
- package/dest/native/fork_checkpoint.d.ts +7 -1
- package/dest/native/fork_checkpoint.d.ts.map +1 -1
- package/dest/native/fork_checkpoint.js +15 -3
- package/dest/native/ipc_world_state_instance.d.ts +89 -0
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
- package/dest/native/ipc_world_state_instance.js +662 -0
- package/dest/native/merkle_trees_facade.d.ts +11 -7
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +62 -114
- package/dest/native/message.d.ts +14 -222
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +0 -42
- package/dest/native/native_world_state.d.ts +19 -7
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +116 -58
- package/dest/native/native_world_state_instance.d.ts +60 -41
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +5 -202
- package/dest/native/world_state_operation.d.ts +7 -0
- package/dest/native/world_state_operation.d.ts.map +1 -0
- package/dest/native/world_state_operation.js +5 -0
- package/dest/synchronizer/config.d.ts +1 -1
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +9 -10
- package/dest/synchronizer/errors.d.ts +8 -1
- package/dest/synchronizer/errors.d.ts.map +1 -1
- package/dest/synchronizer/errors.js +8 -1
- package/dest/synchronizer/factory.d.ts +4 -4
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +7 -6
- package/dest/synchronizer/index.d.ts +2 -1
- package/dest/synchronizer/index.d.ts.map +1 -1
- package/dest/synchronizer/index.js +1 -0
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +13 -8
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +108 -44
- package/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +6 -1
- package/dest/testing.d.ts +4 -3
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +14 -7
- package/dest/world-state-db/merkle_tree_db.d.ts +1 -10
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
- package/package.json +14 -11
- package/src/instrumentation/instrumentation.ts +6 -18
- package/src/native/fork_checkpoint.ts +19 -3
- package/src/native/ipc_world_state_instance.ts +834 -0
- package/src/native/merkle_trees_facade.ts +88 -108
- package/src/native/message.ts +13 -287
- package/src/native/native_world_state.ts +132 -97
- package/src/native/native_world_state_instance.ts +98 -280
- package/src/native/world_state_operation.ts +33 -0
- package/src/synchronizer/config.ts +14 -10
- package/src/synchronizer/errors.ts +8 -0
- package/src/synchronizer/factory.ts +11 -10
- package/src/synchronizer/index.ts +1 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +126 -43
- package/src/test/utils.ts +10 -1
- package/src/testing.ts +12 -10
- package/src/world-state-db/merkle_tree_db.ts +0 -10
- package/dest/native/world_state_ops_queue.d.ts +0 -19
- package/dest/native/world_state_ops_queue.d.ts.map +0 -1
- package/dest/native/world_state_ops_queue.js +0 -146
- package/src/native/world_state_ops_queue.ts +0 -190
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARCHIVE_HEIGHT,
|
|
3
|
+
DomainSeparator,
|
|
4
|
+
L1_TO_L2_MSG_TREE_HEIGHT,
|
|
5
|
+
MAX_NULLIFIERS_PER_TX,
|
|
6
|
+
MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
|
|
7
|
+
NOTE_HASH_TREE_HEIGHT,
|
|
8
|
+
NULLIFIER_TREE_HEIGHT,
|
|
9
|
+
PUBLIC_DATA_TREE_HEIGHT,
|
|
10
|
+
} from '@aztec/constants';
|
|
11
|
+
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
12
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
13
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
14
|
+
import type { IndexedTreeId, TreeInfo } from '@aztec/stdlib/interfaces/server';
|
|
15
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
16
|
+
import type { GenesisData, WorldStateRevision } from '@aztec/stdlib/world-state';
|
|
17
|
+
import { AsyncApi, WsdbService } from '@aztec/wsdb';
|
|
18
|
+
import type {
|
|
19
|
+
WorldStateDBStats as WsdbDBStats,
|
|
20
|
+
DBStats as WsdbDBStatsInner,
|
|
21
|
+
WorldStateMeta as WsdbMeta,
|
|
22
|
+
SiblingPathAndIndex as WsdbSiblingPathAndIndex,
|
|
23
|
+
WorldStateStatusFull as WsdbStatusFull,
|
|
24
|
+
WorldStateStatusSummary as WsdbStatusSummary,
|
|
25
|
+
TreeDBStats as WsdbTreeDBStats,
|
|
26
|
+
TreeMeta as WsdbTreeMeta,
|
|
27
|
+
} from '@aztec/wsdb';
|
|
28
|
+
|
|
29
|
+
import { cpus } from 'node:os';
|
|
30
|
+
|
|
31
|
+
import type { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
|
|
32
|
+
import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js';
|
|
33
|
+
import type {
|
|
34
|
+
BlockStateReference,
|
|
35
|
+
DBStats,
|
|
36
|
+
SerializedBatchInsertionResult,
|
|
37
|
+
SerializedIndexedLeaf,
|
|
38
|
+
SerializedLeafValue,
|
|
39
|
+
SerializedSequentialInsertionResult,
|
|
40
|
+
SerializedSiblingPathAndIndex,
|
|
41
|
+
TreeDBStats,
|
|
42
|
+
TreeMeta,
|
|
43
|
+
TreeStateReference,
|
|
44
|
+
WorldStateDBStats,
|
|
45
|
+
WorldStateMeta,
|
|
46
|
+
WorldStateStatusFull,
|
|
47
|
+
WorldStateStatusSummary,
|
|
48
|
+
} from './message.js';
|
|
49
|
+
import type { NativeWorldStateInstance } from './native_world_state_instance.js';
|
|
50
|
+
import type { WorldStateOperationName } from './world_state_operation.js';
|
|
51
|
+
|
|
52
|
+
// ————— Request conversion helpers —————
|
|
53
|
+
|
|
54
|
+
function toWsdbRevision(rev: WorldStateRevision): { forkId: number; blockNumber: number; includeUncommitted: boolean } {
|
|
55
|
+
return {
|
|
56
|
+
forkId: rev.forkId,
|
|
57
|
+
blockNumber: Number(rev.blockNumber),
|
|
58
|
+
includeUncommitted: rev.includeUncommitted,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function blockStateRefToMap(ref: Map<number, readonly [Buffer, number | bigint]>) {
|
|
63
|
+
return [...ref.entries()].map(([treeId, [root, size]]) => ({
|
|
64
|
+
treeId,
|
|
65
|
+
root: new Uint8Array(root),
|
|
66
|
+
size: Number(size),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toPublicDataLeaf(leaf: SerializedLeafValue): { slot: Uint8Array; value: Uint8Array } {
|
|
71
|
+
if (leaf instanceof Buffer || !('slot' in leaf)) {
|
|
72
|
+
throw new Error('Expected public data leaf');
|
|
73
|
+
}
|
|
74
|
+
return { slot: new Uint8Array(leaf.slot), value: new Uint8Array(leaf.value) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toNullifierLeaf(leaf: SerializedLeafValue): { nullifier: Uint8Array } {
|
|
78
|
+
if (leaf instanceof Buffer || !('nullifier' in leaf)) {
|
|
79
|
+
throw new Error('Expected nullifier leaf');
|
|
80
|
+
}
|
|
81
|
+
return { nullifier: new Uint8Array(leaf.nullifier) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function fromPublicDataLeaf(leaf: { slot: Uint8Array; value: Uint8Array }): Exclude<SerializedLeafValue, Buffer> {
|
|
85
|
+
return { slot: Buffer.from(leaf.slot), value: Buffer.from(leaf.value) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function fromNullifierLeaf(leaf: { nullifier: Uint8Array }): Exclude<SerializedLeafValue, Buffer> {
|
|
89
|
+
return { nullifier: Buffer.from(leaf.nullifier) };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type WireIndexedLeaf<TLeaf> = { leaf: TLeaf; nextIndex: number; nextKey: Uint8Array };
|
|
93
|
+
type WireLeafUpdateWitnessData<TLeaf> = { leaf: WireIndexedLeaf<TLeaf>; index: number; path: Uint8Array[] };
|
|
94
|
+
|
|
95
|
+
function fromIndexedLeaf<TLeaf>(
|
|
96
|
+
leaf: WireIndexedLeaf<TLeaf>,
|
|
97
|
+
convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>,
|
|
98
|
+
) {
|
|
99
|
+
return {
|
|
100
|
+
leaf: convertLeaf(leaf.leaf),
|
|
101
|
+
nextIndex: leaf.nextIndex,
|
|
102
|
+
nextKey: Buffer.from(leaf.nextKey),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function fromLeafUpdateWitnessData<TLeaf>(
|
|
107
|
+
data: WireLeafUpdateWitnessData<TLeaf>,
|
|
108
|
+
convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>,
|
|
109
|
+
) {
|
|
110
|
+
return {
|
|
111
|
+
leaf: fromIndexedLeaf(data.leaf, convertLeaf),
|
|
112
|
+
index: data.index,
|
|
113
|
+
path: data.path.map(p => Buffer.from(p)),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function fromBatchInsertionResult<
|
|
118
|
+
TLeaf,
|
|
119
|
+
TResult extends {
|
|
120
|
+
lowLeafWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
121
|
+
sortedLeaves: Array<{ leaf: TLeaf; index: number }>;
|
|
122
|
+
subtreePath: Uint8Array[];
|
|
123
|
+
},
|
|
124
|
+
>(result: TResult, convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>) {
|
|
125
|
+
return {
|
|
126
|
+
lowLeafWitnessData: result.lowLeafWitnessData.map(data => fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
127
|
+
sortedLeaves: result.sortedLeaves.map(({ leaf, index }) => [convertLeaf(leaf), index] as const),
|
|
128
|
+
subtreePath: result.subtreePath.map(p => Buffer.from(p)),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function fromSequentialInsertionResult<
|
|
133
|
+
TLeaf,
|
|
134
|
+
TResult extends {
|
|
135
|
+
lowLeafWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
136
|
+
insertionWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
137
|
+
},
|
|
138
|
+
>(result: TResult, convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>) {
|
|
139
|
+
return {
|
|
140
|
+
lowLeafWitnessData: result.lowLeafWitnessData.map(data => fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
141
|
+
insertionWitnessData: result.insertionWitnessData.map(data => fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function toFrLeaf(leaf: SerializedLeafValue): Uint8Array {
|
|
146
|
+
if (!(leaf instanceof Buffer)) {
|
|
147
|
+
throw new Error('Expected field leaf');
|
|
148
|
+
}
|
|
149
|
+
return new Uint8Array(leaf);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatMap(map: Record<number, number> | undefined): string | undefined {
|
|
153
|
+
if (!map || Object.keys(map).length === 0) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
return `{${Object.entries(map)
|
|
157
|
+
.map(([key, value]) => `${key}:${value}`)
|
|
158
|
+
.join(',')}}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** SHM request/response ring size for the spawned aztec-wsdb process (32 MiB). */
|
|
162
|
+
const WSDB_SHM_RING_SIZE = 32 * 1024 * 1024;
|
|
163
|
+
|
|
164
|
+
function getWsdbThreadCount(): number {
|
|
165
|
+
return Math.min(16, cpus().length);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getWsdbExtraArgs(
|
|
169
|
+
dataDir: string,
|
|
170
|
+
wsTreeMapSizes: WorldStateTreeMapSizes,
|
|
171
|
+
genesis: GenesisData,
|
|
172
|
+
threads: number,
|
|
173
|
+
): string[] {
|
|
174
|
+
const options = getWsdbOptions(dataDir, wsTreeMapSizes);
|
|
175
|
+
const args = ['--data-dir', dataDir, '--threads', threads.toString()];
|
|
176
|
+
|
|
177
|
+
// Size the SHM rings generously. Responses such as batch-insertion witnesses
|
|
178
|
+
// run to a few MB, and an SHM frame is capped at half the ring (the wrap
|
|
179
|
+
// handling needs that headroom), so 32 MiB rings give ~16 MiB per message.
|
|
180
|
+
// Ignored by the UDS transport. Pages are demand-faulted, so unused capacity
|
|
181
|
+
// is cheap.
|
|
182
|
+
args.push('--request-ring-size', WSDB_SHM_RING_SIZE.toString());
|
|
183
|
+
args.push('--response-ring-size', WSDB_SHM_RING_SIZE.toString());
|
|
184
|
+
|
|
185
|
+
const treeHeights = formatMap(options.treeHeights);
|
|
186
|
+
if (treeHeights) {
|
|
187
|
+
args.push('--tree-heights', treeHeights);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const treePrefill = formatMap(options.treePrefill);
|
|
191
|
+
if (treePrefill) {
|
|
192
|
+
args.push('--tree-prefill', treePrefill);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const mapSizes = formatMap(options.mapSizes);
|
|
196
|
+
if (mapSizes) {
|
|
197
|
+
args.push('--map-sizes', mapSizes);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
args.push('--initial-header-generator-point', options.initialHeaderGeneratorPoint.toString());
|
|
201
|
+
|
|
202
|
+
if (genesis.prefilledPublicData.length > 0) {
|
|
203
|
+
const pairs = genesis.prefilledPublicData.map(data => [
|
|
204
|
+
data.slot.toBuffer().toString('hex'),
|
|
205
|
+
data.value.toBuffer().toString('hex'),
|
|
206
|
+
]);
|
|
207
|
+
args.push('--prefilled-public-data', JSON.stringify(pairs));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const genesisTimestamp = Number(genesis.genesisTimestamp);
|
|
211
|
+
if (genesisTimestamp !== 0) {
|
|
212
|
+
args.push('--genesis-timestamp', genesisTimestamp.toString());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return args;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ————— Response conversion helpers —————
|
|
219
|
+
|
|
220
|
+
/** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */
|
|
221
|
+
function convertUint8ArraysToBuffers(obj: unknown): unknown {
|
|
222
|
+
if (obj instanceof Uint8Array) {
|
|
223
|
+
return Buffer.from(obj);
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(obj)) {
|
|
226
|
+
return obj.map(convertUint8ArraysToBuffers);
|
|
227
|
+
}
|
|
228
|
+
if (obj !== null && typeof obj === 'object') {
|
|
229
|
+
const result: Record<string, unknown> = {};
|
|
230
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
231
|
+
result[key] = convertUint8ArraysToBuffers(value);
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
return obj;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */
|
|
239
|
+
function convertStateRef(
|
|
240
|
+
state: Array<{ treeId: number; root: Uint8Array; size: number }>,
|
|
241
|
+
): Record<number, readonly [Buffer, number | bigint]> {
|
|
242
|
+
const result: Record<number, readonly [Buffer, number | bigint]> = {};
|
|
243
|
+
for (const { treeId, root, size } of state) {
|
|
244
|
+
result[treeId] = [Buffer.from(root), BigInt(size)] as const;
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Convert Wsdb WorldStateStatusSummary to the native world-state format. */
|
|
250
|
+
function convertStatusSummary(s: WsdbStatusSummary): WorldStateStatusSummary {
|
|
251
|
+
return {
|
|
252
|
+
unfinalizedBlockNumber: s.unfinalizedBlockNumber,
|
|
253
|
+
finalizedBlockNumber: s.finalizedBlockNumber,
|
|
254
|
+
oldestHistoricalBlock: s.oldestHistoricalBlock,
|
|
255
|
+
treesAreSynched: s.treesAreSynched,
|
|
256
|
+
} as unknown as WorldStateStatusSummary;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function convertDBStats(s: WsdbDBStatsInner): DBStats {
|
|
260
|
+
return {
|
|
261
|
+
name: s.name,
|
|
262
|
+
numDataItems: s.numDataItems,
|
|
263
|
+
totalUsedSize: s.totalUsedSize,
|
|
264
|
+
} as unknown as DBStats;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function convertTreeDBStats(s: WsdbTreeDBStats): TreeDBStats {
|
|
268
|
+
return {
|
|
269
|
+
mapSize: s.mapSize,
|
|
270
|
+
physicalFileSize: s.physicalFileSize,
|
|
271
|
+
blocksDBStats: convertDBStats(s.blocksDBStats),
|
|
272
|
+
nodesDBStats: convertDBStats(s.nodesDBStats),
|
|
273
|
+
leafPreimagesDBStats: convertDBStats(s.leafPreimagesDBStats),
|
|
274
|
+
leafIndicesDBStats: convertDBStats(s.leafIndicesDBStats),
|
|
275
|
+
blockIndicesDBStats: convertDBStats(s.blockIndicesDBStats),
|
|
276
|
+
} as unknown as TreeDBStats;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function convertWorldStateDBStats(s: WsdbDBStats): WorldStateDBStats {
|
|
280
|
+
return {
|
|
281
|
+
noteHashTreeStats: convertTreeDBStats(s.noteHashTreeStats),
|
|
282
|
+
messageTreeStats: convertTreeDBStats(s.messageTreeStats),
|
|
283
|
+
archiveTreeStats: convertTreeDBStats(s.archiveTreeStats),
|
|
284
|
+
publicDataTreeStats: convertTreeDBStats(s.publicDataTreeStats),
|
|
285
|
+
nullifierTreeStats: convertTreeDBStats(s.nullifierTreeStats),
|
|
286
|
+
} as unknown as WorldStateDBStats;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function convertTreeMeta(m: WsdbTreeMeta): TreeMeta {
|
|
290
|
+
return {
|
|
291
|
+
name: m.name,
|
|
292
|
+
depth: m.depth,
|
|
293
|
+
size: m.size,
|
|
294
|
+
committedSize: m.committedSize,
|
|
295
|
+
root: m.root,
|
|
296
|
+
initialSize: m.initialSize,
|
|
297
|
+
initialRoot: m.initialRoot,
|
|
298
|
+
oldestHistoricBlock: m.oldestHistoricBlock,
|
|
299
|
+
unfinalizedBlockHeight: m.unfinalizedBlockHeight,
|
|
300
|
+
finalizedBlockHeight: m.finalizedBlockHeight,
|
|
301
|
+
} as unknown as TreeMeta;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function convertWorldStateMeta(m: WsdbMeta): WorldStateMeta {
|
|
305
|
+
return {
|
|
306
|
+
noteHashTreeMeta: convertTreeMeta(m.noteHashTreeMeta),
|
|
307
|
+
messageTreeMeta: convertTreeMeta(m.messageTreeMeta),
|
|
308
|
+
archiveTreeMeta: convertTreeMeta(m.archiveTreeMeta),
|
|
309
|
+
publicDataTreeMeta: convertTreeMeta(m.publicDataTreeMeta),
|
|
310
|
+
nullifierTreeMeta: convertTreeMeta(m.nullifierTreeMeta),
|
|
311
|
+
} as unknown as WorldStateMeta;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function convertStatusFull(s: WsdbStatusFull): WorldStateStatusFull {
|
|
315
|
+
return {
|
|
316
|
+
summary: convertStatusSummary(s.summary),
|
|
317
|
+
dbStats: convertWorldStateDBStats(s.dbStats),
|
|
318
|
+
meta: convertWorldStateMeta(s.meta),
|
|
319
|
+
} as unknown as WorldStateStatusFull;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Convert Wsdb SiblingPathAndIndex to NAPI format. */
|
|
323
|
+
function convertSiblingPathAndIndex(
|
|
324
|
+
s: WsdbSiblingPathAndIndex | null | undefined,
|
|
325
|
+
): { index: bigint; path: Buffer[] } | undefined {
|
|
326
|
+
if (!s) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
index: BigInt(s.index),
|
|
331
|
+
path: s.path.map(p => Buffer.from(p)),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ————— Public API —————
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* IPC-backed world state instance.
|
|
339
|
+
* Uses WsdbService (spawns aztec-wsdb binary) and the generated AsyncApi
|
|
340
|
+
* to communicate via the NamedUnion IPC protocol.
|
|
341
|
+
*/
|
|
342
|
+
export class IpcWorldState implements NativeWorldStateInstance {
|
|
343
|
+
private open = true;
|
|
344
|
+
private api: AsyncApi;
|
|
345
|
+
/** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */
|
|
346
|
+
private checkpointDepths = new Map<number, number>();
|
|
347
|
+
|
|
348
|
+
constructor(
|
|
349
|
+
private readonly wsdb: WsdbService,
|
|
350
|
+
private readonly instrumentation: WorldStateInstrumentation,
|
|
351
|
+
bindings?: LoggerBindings,
|
|
352
|
+
private readonly log: Logger = createLogger('world-state:ipc-database', bindings),
|
|
353
|
+
) {
|
|
354
|
+
this.api = wsdb;
|
|
355
|
+
this.log.info('Created IPC-backed world state instance');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Spawn an `aztec-wsdb` subprocess and return an IPC-backed world state wrapping it.
|
|
360
|
+
* Encapsulates wsdb binary discovery, service construction, and readiness wait.
|
|
361
|
+
*/
|
|
362
|
+
static async spawn(
|
|
363
|
+
dataDir: string,
|
|
364
|
+
wsTreeMapSizes: WorldStateTreeMapSizes,
|
|
365
|
+
genesis: GenesisData,
|
|
366
|
+
instrumentation: WorldStateInstrumentation,
|
|
367
|
+
bindings?: LoggerBindings,
|
|
368
|
+
): Promise<IpcWorldState> {
|
|
369
|
+
const threads = getWsdbThreadCount();
|
|
370
|
+
const transport = process.env.WSDB_TRANSPORT === 'shm' ? 'shm' : 'uds';
|
|
371
|
+
const wsdb = await WsdbService.spawn({
|
|
372
|
+
transport,
|
|
373
|
+
extraArgs: getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads),
|
|
374
|
+
env: { HARDWARE_CONCURRENCY: threads.toString() },
|
|
375
|
+
});
|
|
376
|
+
return new IpcWorldState(wsdb, instrumentation, bindings);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
getIpcPath(): string {
|
|
380
|
+
return this.wsdb.getIpcPath();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async close(): Promise<void> {
|
|
384
|
+
if (!this.open) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.open = false;
|
|
388
|
+
await this.wsdb.destroy();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
getTreeInfo(treeId: MerkleTreeId, revision: WorldStateRevision): Promise<TreeInfo> {
|
|
392
|
+
return this.execute('getTreeInfo', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
393
|
+
const resp = await this.api.getTreeInfo({ treeId, revision: toWsdbRevision(revision) });
|
|
394
|
+
return {
|
|
395
|
+
treeId: resp.treeId,
|
|
396
|
+
root: Buffer.from(resp.root),
|
|
397
|
+
size: BigInt(resp.size),
|
|
398
|
+
depth: resp.depth,
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
getStateReference(revision: WorldStateRevision): Promise<Record<number, TreeStateReference>> {
|
|
404
|
+
return this.execute('getStateReference', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
405
|
+
const resp = await this.api.getStateReference({ revision: toWsdbRevision(revision) });
|
|
406
|
+
return convertStateRef(resp.state);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
getInitialStateReference(): Promise<Record<number, TreeStateReference>> {
|
|
411
|
+
return this.execute('getInitialStateReference', 0, false, async () => {
|
|
412
|
+
const resp = await this.api.getInitialStateReference({});
|
|
413
|
+
return convertStateRef(resp.state);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
getLeafValue(
|
|
418
|
+
treeId: MerkleTreeId,
|
|
419
|
+
revision: WorldStateRevision,
|
|
420
|
+
leafIndex: bigint,
|
|
421
|
+
): Promise<SerializedLeafValue | undefined> {
|
|
422
|
+
return this.execute('getLeafValue', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
423
|
+
const wsdbRevision = toWsdbRevision(revision);
|
|
424
|
+
const wsdbLeafIndex = Number(leafIndex);
|
|
425
|
+
|
|
426
|
+
if (treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
|
|
427
|
+
const resp = await this.api.getPublicDataLeafValue({ revision: wsdbRevision, leafIndex: wsdbLeafIndex });
|
|
428
|
+
return resp.value ? fromPublicDataLeaf(resp.value) : undefined;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (treeId === MerkleTreeId.NULLIFIER_TREE) {
|
|
432
|
+
const resp = await this.api.getNullifierLeafValue({ revision: wsdbRevision, leafIndex: wsdbLeafIndex });
|
|
433
|
+
return resp.value ? fromNullifierLeaf(resp.value) : undefined;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const resp = await this.api.getLeafValue({ treeId, revision: wsdbRevision, leafIndex: wsdbLeafIndex });
|
|
437
|
+
return resp.value ? Buffer.from(resp.value) : undefined;
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
getLeafPreimage(
|
|
442
|
+
treeId: IndexedTreeId,
|
|
443
|
+
revision: WorldStateRevision,
|
|
444
|
+
leafIndex: bigint,
|
|
445
|
+
): Promise<SerializedIndexedLeaf | undefined> {
|
|
446
|
+
return this.execute('getLeafPreimage', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
447
|
+
const resp =
|
|
448
|
+
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
449
|
+
? await this.api.getPublicDataLeafPreimage({
|
|
450
|
+
revision: toWsdbRevision(revision),
|
|
451
|
+
leafIndex: Number(leafIndex),
|
|
452
|
+
})
|
|
453
|
+
: await this.api.getNullifierLeafPreimage({
|
|
454
|
+
revision: toWsdbRevision(revision),
|
|
455
|
+
leafIndex: Number(leafIndex),
|
|
456
|
+
});
|
|
457
|
+
return resp.preimage ? (convertUint8ArraysToBuffers(resp.preimage) as SerializedIndexedLeaf) : undefined;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
getSiblingPath(treeId: MerkleTreeId, revision: WorldStateRevision, leafIndex: bigint): Promise<Buffer[]> {
|
|
462
|
+
return this.execute('getSiblingPath', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
463
|
+
const resp = await this.api.getSiblingPath({
|
|
464
|
+
treeId,
|
|
465
|
+
revision: toWsdbRevision(revision),
|
|
466
|
+
leafIndex: Number(leafIndex),
|
|
467
|
+
});
|
|
468
|
+
return resp.path.map(p => Buffer.from(p));
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
getBlockNumbersForLeafIndices(
|
|
473
|
+
treeId: MerkleTreeId,
|
|
474
|
+
revision: WorldStateRevision,
|
|
475
|
+
leafIndices: bigint[],
|
|
476
|
+
): Promise<(bigint | undefined)[]> {
|
|
477
|
+
return this.execute(
|
|
478
|
+
'getBlockNumbersForLeafIndices',
|
|
479
|
+
revision.forkId,
|
|
480
|
+
revision.includeUncommitted === false,
|
|
481
|
+
async () => {
|
|
482
|
+
const resp = await this.api.getBlockNumbersForLeafIndices({
|
|
483
|
+
treeId,
|
|
484
|
+
revision: toWsdbRevision(revision),
|
|
485
|
+
leafIndices: leafIndices.map(Number),
|
|
486
|
+
});
|
|
487
|
+
return resp.blockNumbers.map(n => (n != null ? BigInt(n) : undefined));
|
|
488
|
+
},
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
findLeafIndices(
|
|
493
|
+
treeId: MerkleTreeId,
|
|
494
|
+
revision: WorldStateRevision,
|
|
495
|
+
leaves: SerializedLeafValue[],
|
|
496
|
+
startIndex: bigint,
|
|
497
|
+
): Promise<(bigint | undefined)[]> {
|
|
498
|
+
return this.execute('findLeafIndices', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
499
|
+
const wsdbRevision = toWsdbRevision(revision);
|
|
500
|
+
const wsdbStartIndex = Number(startIndex);
|
|
501
|
+
const resp =
|
|
502
|
+
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
503
|
+
? await this.api.findPublicDataLeafIndices({
|
|
504
|
+
revision: wsdbRevision,
|
|
505
|
+
leaves: leaves.map(toPublicDataLeaf),
|
|
506
|
+
startIndex: wsdbStartIndex,
|
|
507
|
+
})
|
|
508
|
+
: treeId === MerkleTreeId.NULLIFIER_TREE
|
|
509
|
+
? await this.api.findNullifierLeafIndices({
|
|
510
|
+
revision: wsdbRevision,
|
|
511
|
+
leaves: leaves.map(toNullifierLeaf),
|
|
512
|
+
startIndex: wsdbStartIndex,
|
|
513
|
+
})
|
|
514
|
+
: await this.api.findLeafIndices({
|
|
515
|
+
treeId,
|
|
516
|
+
revision: wsdbRevision,
|
|
517
|
+
leaves: leaves.map(toFrLeaf),
|
|
518
|
+
startIndex: wsdbStartIndex,
|
|
519
|
+
});
|
|
520
|
+
return resp.indices.map(n => (n != null ? BigInt(n) : undefined));
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
findLowLeaf(
|
|
525
|
+
treeId: IndexedTreeId,
|
|
526
|
+
revision: WorldStateRevision,
|
|
527
|
+
key: Fr,
|
|
528
|
+
): Promise<{ index: bigint; alreadyPresent: boolean }> {
|
|
529
|
+
return this.execute('findLowLeaf', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
530
|
+
const resp = await this.api.findLowLeaf({
|
|
531
|
+
treeId,
|
|
532
|
+
revision: toWsdbRevision(revision),
|
|
533
|
+
key: new Uint8Array(key.toBuffer()),
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
alreadyPresent: resp.alreadyPresent,
|
|
537
|
+
index: BigInt(resp.index),
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
findSiblingPaths(
|
|
543
|
+
treeId: MerkleTreeId,
|
|
544
|
+
revision: WorldStateRevision,
|
|
545
|
+
leaves: SerializedLeafValue[],
|
|
546
|
+
): Promise<(SerializedSiblingPathAndIndex | undefined)[]> {
|
|
547
|
+
return this.execute('findSiblingPaths', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
548
|
+
const wsdbRevision = toWsdbRevision(revision);
|
|
549
|
+
const resp =
|
|
550
|
+
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
551
|
+
? await this.api.findPublicDataSiblingPaths({
|
|
552
|
+
revision: wsdbRevision,
|
|
553
|
+
leaves: leaves.map(toPublicDataLeaf),
|
|
554
|
+
})
|
|
555
|
+
: treeId === MerkleTreeId.NULLIFIER_TREE
|
|
556
|
+
? await this.api.findNullifierSiblingPaths({
|
|
557
|
+
revision: wsdbRevision,
|
|
558
|
+
leaves: leaves.map(toNullifierLeaf),
|
|
559
|
+
})
|
|
560
|
+
: await this.api.findSiblingPaths({
|
|
561
|
+
treeId,
|
|
562
|
+
revision: wsdbRevision,
|
|
563
|
+
leaves: leaves.map(toFrLeaf),
|
|
564
|
+
});
|
|
565
|
+
return resp.paths.map(convertSiblingPathAndIndex);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
updateArchive(forkId: number, blockStateRef: BlockStateReference, blockHeaderHash: Buffer): Promise<void> {
|
|
570
|
+
return this.execute('updateArchive', forkId, false, async () => {
|
|
571
|
+
await this.api.updateArchive({
|
|
572
|
+
blockStateRef: blockStateRefToMap(blockStateRef),
|
|
573
|
+
blockHeaderHash: new Uint8Array(blockHeaderHash),
|
|
574
|
+
forkId,
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
appendLeaves(treeId: MerkleTreeId, forkId: number, leaves: SerializedLeafValue[]): Promise<void> {
|
|
580
|
+
return this.execute('appendLeaves', forkId, false, async () => {
|
|
581
|
+
if (treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
|
|
582
|
+
await this.api.appendPublicDataLeaves({ leaves: leaves.map(toPublicDataLeaf), forkId });
|
|
583
|
+
} else if (treeId === MerkleTreeId.NULLIFIER_TREE) {
|
|
584
|
+
await this.api.appendNullifierLeaves({ leaves: leaves.map(toNullifierLeaf), forkId });
|
|
585
|
+
} else {
|
|
586
|
+
await this.api.appendLeaves({ treeId, leaves: leaves.map(toFrLeaf), forkId });
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
batchInsert(
|
|
592
|
+
treeId: IndexedTreeId,
|
|
593
|
+
forkId: number,
|
|
594
|
+
leaves: SerializedLeafValue[],
|
|
595
|
+
subtreeDepth: number,
|
|
596
|
+
): Promise<SerializedBatchInsertionResult> {
|
|
597
|
+
return this.execute('batchInsert', forkId, false, async () => {
|
|
598
|
+
const resp =
|
|
599
|
+
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
600
|
+
? await this.api.batchInsertPublicData({
|
|
601
|
+
leaves: leaves.map(toPublicDataLeaf),
|
|
602
|
+
subtreeDepth,
|
|
603
|
+
forkId,
|
|
604
|
+
})
|
|
605
|
+
: await this.api.batchInsertNullifier({
|
|
606
|
+
leaves: leaves.map(toNullifierLeaf),
|
|
607
|
+
subtreeDepth,
|
|
608
|
+
forkId,
|
|
609
|
+
});
|
|
610
|
+
return treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
611
|
+
? fromBatchInsertionResult(resp.result as any, fromPublicDataLeaf)
|
|
612
|
+
: fromBatchInsertionResult(resp.result as any, fromNullifierLeaf);
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
sequentialInsert(
|
|
617
|
+
treeId: IndexedTreeId,
|
|
618
|
+
forkId: number,
|
|
619
|
+
leaves: SerializedLeafValue[],
|
|
620
|
+
): Promise<SerializedSequentialInsertionResult> {
|
|
621
|
+
return this.execute('sequentialInsert', forkId, false, async () => {
|
|
622
|
+
const resp =
|
|
623
|
+
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
624
|
+
? await this.api.sequentialInsertPublicData({
|
|
625
|
+
leaves: leaves.map(toPublicDataLeaf),
|
|
626
|
+
forkId,
|
|
627
|
+
})
|
|
628
|
+
: await this.api.sequentialInsertNullifier({
|
|
629
|
+
leaves: leaves.map(toNullifierLeaf),
|
|
630
|
+
forkId,
|
|
631
|
+
});
|
|
632
|
+
return treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
633
|
+
? fromSequentialInsertionResult(resp.result as any, fromPublicDataLeaf)
|
|
634
|
+
: fromSequentialInsertionResult(resp.result as any, fromNullifierLeaf);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
syncBlock(input: {
|
|
639
|
+
blockNumber: BlockNumber;
|
|
640
|
+
blockStateRef: BlockStateReference;
|
|
641
|
+
blockHeaderHash: Buffer;
|
|
642
|
+
expectedArchiveRoot: Buffer;
|
|
643
|
+
expectedPreviousArchiveRoot: Buffer;
|
|
644
|
+
paddedNoteHashes: readonly SerializedLeafValue[];
|
|
645
|
+
paddedL1ToL2Messages: readonly SerializedLeafValue[];
|
|
646
|
+
paddedNullifiers: readonly SerializedLeafValue[];
|
|
647
|
+
publicDataWrites: readonly SerializedLeafValue[];
|
|
648
|
+
}): Promise<WorldStateStatusFull> {
|
|
649
|
+
return this.execute('syncBlock', 0, false, async () => {
|
|
650
|
+
const resp = await this.api.syncBlock({
|
|
651
|
+
blockNumber: Number(input.blockNumber),
|
|
652
|
+
blockStateRef: blockStateRefToMap(input.blockStateRef),
|
|
653
|
+
blockHeaderHash: new Uint8Array(input.blockHeaderHash),
|
|
654
|
+
expectedArchiveRoot: new Uint8Array(input.expectedArchiveRoot),
|
|
655
|
+
expectedPreviousArchiveRoot: new Uint8Array(input.expectedPreviousArchiveRoot),
|
|
656
|
+
paddedNoteHashes: input.paddedNoteHashes.map(toFrLeaf),
|
|
657
|
+
paddedL1ToL2Messages: input.paddedL1ToL2Messages.map(toFrLeaf),
|
|
658
|
+
paddedNullifiers: input.paddedNullifiers.map(toNullifierLeaf),
|
|
659
|
+
publicDataWrites: input.publicDataWrites.map(toPublicDataLeaf),
|
|
660
|
+
});
|
|
661
|
+
return convertStatusFull(resp.status);
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
createFork(input: { latest: boolean; blockNumber: BlockNumber }): Promise<number> {
|
|
666
|
+
return this.execute('createFork', 0, false, async () => {
|
|
667
|
+
const resp = await this.api.createFork({
|
|
668
|
+
latest: input.latest,
|
|
669
|
+
blockNumber: Number(input.blockNumber),
|
|
670
|
+
});
|
|
671
|
+
return resp.forkId;
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
deleteFork(forkId: number): Promise<void> {
|
|
676
|
+
return this.execute('deleteFork', forkId, false, async () => {
|
|
677
|
+
await this.api.deleteFork({ forkId });
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
finalizeBlocks(toBlockNumber: BlockNumber): Promise<WorldStateStatusSummary> {
|
|
682
|
+
return this.execute('finalizeBlocks', 0, false, async () => {
|
|
683
|
+
const resp = await this.api.finalizeBlocks({ toBlockNumber: Number(toBlockNumber) });
|
|
684
|
+
return convertStatusSummary(resp.status);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
unwindBlocks(toBlockNumber: BlockNumber): Promise<WorldStateStatusFull> {
|
|
689
|
+
return this.execute('unwindBlocks', 0, false, async () => {
|
|
690
|
+
const resp = await this.api.unwindBlocks({ toBlockNumber: Number(toBlockNumber) });
|
|
691
|
+
return convertStatusFull(resp.status);
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
removeHistoricalBlocks(toBlockNumber: BlockNumber): Promise<WorldStateStatusFull> {
|
|
696
|
+
return this.execute('removeHistoricalBlocks', 0, false, async () => {
|
|
697
|
+
const resp = await this.api.removeHistoricalBlocks({ toBlockNumber: Number(toBlockNumber) });
|
|
698
|
+
return convertStatusFull(resp.status);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
getStatus(): Promise<WorldStateStatusSummary> {
|
|
703
|
+
return this.execute('getStatus', 0, false, async () => {
|
|
704
|
+
const resp = await this.api.getStatus({});
|
|
705
|
+
return convertStatusSummary(resp.status);
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
createCheckpoint(forkId: number): Promise<number> {
|
|
710
|
+
return this.execute('createCheckpoint', forkId, false, async () => {
|
|
711
|
+
await this.api.createCheckpoint({ forkId });
|
|
712
|
+
const depth = (this.checkpointDepths.get(forkId) ?? 0) + 1;
|
|
713
|
+
this.checkpointDepths.set(forkId, depth);
|
|
714
|
+
return depth;
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
commitCheckpoint(forkId: number): Promise<void> {
|
|
719
|
+
return this.execute('commitCheckpoint', forkId, false, async () => {
|
|
720
|
+
await this.api.commitCheckpoint({ forkId });
|
|
721
|
+
const depth = Math.max(0, (this.checkpointDepths.get(forkId) ?? 0) - 1);
|
|
722
|
+
this.checkpointDepths.set(forkId, depth);
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
revertCheckpoint(forkId: number): Promise<void> {
|
|
727
|
+
return this.execute('revertCheckpoint', forkId, false, async () => {
|
|
728
|
+
await this.api.revertCheckpoint({ forkId });
|
|
729
|
+
const depth = Math.max(0, (this.checkpointDepths.get(forkId) ?? 0) - 1);
|
|
730
|
+
this.checkpointDepths.set(forkId, depth);
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
commitAllCheckpoints(forkId: number, depth: number): Promise<void> {
|
|
735
|
+
return this.execute('commitAllCheckpoints', forkId, false, async () => {
|
|
736
|
+
const currentDepth = this.checkpointDepths.get(forkId) ?? 0;
|
|
737
|
+
if (depth === 0) {
|
|
738
|
+
await this.api.commitAllCheckpoints({ forkId });
|
|
739
|
+
} else {
|
|
740
|
+
for (let d = currentDepth; d > depth; d--) {
|
|
741
|
+
await this.api.commitCheckpoint({ forkId });
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
this.checkpointDepths.set(forkId, depth);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
revertAllCheckpoints(forkId: number, depth: number): Promise<void> {
|
|
749
|
+
return this.execute('revertAllCheckpoints', forkId, false, async () => {
|
|
750
|
+
const currentDepth = this.checkpointDepths.get(forkId) ?? 0;
|
|
751
|
+
if (depth === 0) {
|
|
752
|
+
await this.api.revertAllCheckpoints({ forkId });
|
|
753
|
+
} else {
|
|
754
|
+
for (let d = currentDepth; d > depth; d--) {
|
|
755
|
+
await this.api.revertCheckpoint({ forkId });
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
this.checkpointDepths.set(forkId, depth);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
copyStores(dstPath: string, compact: boolean): Promise<void> {
|
|
763
|
+
return this.execute('copyStores', 0, false, async () => {
|
|
764
|
+
await this.api.copyStores({ dstPath, compact });
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Read/write ordering is enforced server-side by the wsdb per-fork scheduler,
|
|
769
|
+
// so the client no longer queues: every op is sent immediately and the server
|
|
770
|
+
// serializes writes per fork while running reads concurrently. `_forkId` and
|
|
771
|
+
// `_committedOnly` are retained in the signature (they describe the op) but are
|
|
772
|
+
// no longer needed for client-side ordering.
|
|
773
|
+
private async execute<T>(
|
|
774
|
+
operation: WorldStateOperationName,
|
|
775
|
+
_forkId: number,
|
|
776
|
+
_committedOnly: boolean,
|
|
777
|
+
request: () => Promise<T>,
|
|
778
|
+
): Promise<T> {
|
|
779
|
+
if (!this.open) {
|
|
780
|
+
throw new Error('IPC instance is closed');
|
|
781
|
+
}
|
|
782
|
+
return await this.send(operation, request);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
private async send<T>(operation: WorldStateOperationName, request: () => Promise<T>): Promise<T> {
|
|
786
|
+
const start = performance.now();
|
|
787
|
+
try {
|
|
788
|
+
const response = await request();
|
|
789
|
+
const durationMs = performance.now() - start;
|
|
790
|
+
this.log.trace(`Call ${operation} took (ms)`, { duration: durationMs });
|
|
791
|
+
this.instrumentation.recordRoundTrip(durationMs * 1000, operation);
|
|
792
|
+
return response;
|
|
793
|
+
} catch (error) {
|
|
794
|
+
this.log.error(`Call ${operation} failed: ${error}`, error);
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Helper to create WsdbOptions from standard world state config.
|
|
802
|
+
* Returns the options needed to construct a wsdb service command line.
|
|
803
|
+
*/
|
|
804
|
+
export function getWsdbOptions(
|
|
805
|
+
dataDir: string,
|
|
806
|
+
wsTreeMapSizes: WorldStateTreeMapSizes,
|
|
807
|
+
): {
|
|
808
|
+
treeHeights: Record<number, number>;
|
|
809
|
+
treePrefill: Record<number, number>;
|
|
810
|
+
mapSizes: Record<number, number>;
|
|
811
|
+
initialHeaderGeneratorPoint: number;
|
|
812
|
+
} {
|
|
813
|
+
return {
|
|
814
|
+
treeHeights: {
|
|
815
|
+
[MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
|
|
816
|
+
[MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
|
|
817
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
|
|
818
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
|
|
819
|
+
[MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT,
|
|
820
|
+
},
|
|
821
|
+
treePrefill: {
|
|
822
|
+
[MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
|
|
823
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
|
|
824
|
+
},
|
|
825
|
+
mapSizes: {
|
|
826
|
+
[MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
|
|
827
|
+
[MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
|
|
828
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
|
|
829
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
|
|
830
|
+
[MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb,
|
|
831
|
+
},
|
|
832
|
+
initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH,
|
|
833
|
+
};
|
|
834
|
+
}
|