@aztec/world-state 0.0.1-commit.001888fc → 0.0.1-commit.017a351
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/native/ipc_world_state_instance.d.ts +45 -0
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
- package/dest/native/ipc_world_state_instance.js +750 -0
- package/dest/native/merkle_trees_facade.d.ts +8 -4
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +34 -15
- package/dest/native/message.d.ts +7 -8
- package/dest/native/message.d.ts.map +1 -1
- 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 +89 -24
- package/dest/native/native_world_state_instance.d.ts +21 -39
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +8 -202
- package/dest/native/world_state_ops_queue.js +5 -5
- 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/factory.d.ts +4 -4
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +7 -6
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +40 -11
- package/dest/testing.d.ts +4 -3
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +10 -6
- package/package.json +13 -10
- package/src/native/ipc_world_state_instance.ts +863 -0
- package/src/native/merkle_trees_facade.ts +41 -19
- package/src/native/message.ts +6 -7
- package/src/native/native_world_state.ts +98 -35
- package/src/native/native_world_state_instance.ts +28 -276
- package/src/native/world_state_ops_queue.ts +5 -5
- package/src/synchronizer/config.ts +14 -10
- package/src/synchronizer/factory.ts +11 -10
- package/src/synchronizer/server_world_state_synchronizer.ts +35 -13
- package/src/testing.ts +8 -9
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
import { ARCHIVE_HEIGHT, DomainSeparator, L1_TO_L2_MSG_TREE_HEIGHT, MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT } from '@aztec/constants';
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
4
|
+
import { WsdbService } from '@aztec/wsdb';
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
import { cpus } from 'node:os';
|
|
7
|
+
import { WorldStateMessageType, isWithCanonical, isWithForkId, isWithRevision } from './message.js';
|
|
8
|
+
import { WorldStateOpsQueue } from './world_state_ops_queue.js';
|
|
9
|
+
// ————— Request conversion helpers —————
|
|
10
|
+
function toWsdbRevision(rev) {
|
|
11
|
+
return {
|
|
12
|
+
forkId: rev.forkId,
|
|
13
|
+
blockNumber: Number(rev.blockNumber),
|
|
14
|
+
includeUncommitted: rev.includeUncommitted
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function blockStateRefToMap(ref) {
|
|
18
|
+
return [
|
|
19
|
+
...ref.entries()
|
|
20
|
+
].map(([treeId, [root, size]])=>({
|
|
21
|
+
treeId,
|
|
22
|
+
root: new Uint8Array(root),
|
|
23
|
+
size: Number(size)
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
function toPublicDataLeaf(leaf) {
|
|
27
|
+
if (leaf instanceof Buffer || !('slot' in leaf)) {
|
|
28
|
+
throw new Error('Expected public data leaf');
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
slot: new Uint8Array(leaf.slot),
|
|
32
|
+
value: new Uint8Array(leaf.value)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function toNullifierLeaf(leaf) {
|
|
36
|
+
if (leaf instanceof Buffer || !('nullifier' in leaf)) {
|
|
37
|
+
throw new Error('Expected nullifier leaf');
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
nullifier: new Uint8Array(leaf.nullifier)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function fromPublicDataLeaf(leaf) {
|
|
44
|
+
return {
|
|
45
|
+
slot: Buffer.from(leaf.slot),
|
|
46
|
+
value: Buffer.from(leaf.value)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function fromNullifierLeaf(leaf) {
|
|
50
|
+
return {
|
|
51
|
+
nullifier: Buffer.from(leaf.nullifier)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function fromIndexedLeaf(leaf, convertLeaf) {
|
|
55
|
+
return {
|
|
56
|
+
leaf: convertLeaf(leaf.leaf),
|
|
57
|
+
nextIndex: leaf.nextIndex,
|
|
58
|
+
nextKey: Buffer.from(leaf.nextKey)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function fromLeafUpdateWitnessData(data, convertLeaf) {
|
|
62
|
+
return {
|
|
63
|
+
leaf: fromIndexedLeaf(data.leaf, convertLeaf),
|
|
64
|
+
index: data.index,
|
|
65
|
+
path: data.path.map((p)=>Buffer.from(p))
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function fromBatchInsertionResult(result, convertLeaf) {
|
|
69
|
+
return {
|
|
70
|
+
lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
71
|
+
sortedLeaves: result.sortedLeaves.map(({ leaf, index })=>[
|
|
72
|
+
convertLeaf(leaf),
|
|
73
|
+
index
|
|
74
|
+
]),
|
|
75
|
+
subtreePath: result.subtreePath.map((p)=>Buffer.from(p))
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function fromSequentialInsertionResult(result, convertLeaf) {
|
|
79
|
+
return {
|
|
80
|
+
lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
81
|
+
insertionWitnessData: result.insertionWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf))
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function toFrLeaf(leaf) {
|
|
85
|
+
if (!(leaf instanceof Buffer)) {
|
|
86
|
+
throw new Error('Expected field leaf');
|
|
87
|
+
}
|
|
88
|
+
return new Uint8Array(leaf);
|
|
89
|
+
}
|
|
90
|
+
function formatMap(map) {
|
|
91
|
+
if (!map || Object.keys(map).length === 0) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
return `{${Object.entries(map).map(([key, value])=>`${key}:${value}`).join(',')}}`;
|
|
95
|
+
}
|
|
96
|
+
function getWsdbThreadCount() {
|
|
97
|
+
return Math.min(16, cpus().length);
|
|
98
|
+
}
|
|
99
|
+
function getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads) {
|
|
100
|
+
const options = getWsdbOptions(dataDir, wsTreeMapSizes);
|
|
101
|
+
const args = [
|
|
102
|
+
'--data-dir',
|
|
103
|
+
dataDir,
|
|
104
|
+
'--threads',
|
|
105
|
+
threads.toString()
|
|
106
|
+
];
|
|
107
|
+
const treeHeights = formatMap(options.treeHeights);
|
|
108
|
+
if (treeHeights) {
|
|
109
|
+
args.push('--tree-heights', treeHeights);
|
|
110
|
+
}
|
|
111
|
+
const treePrefill = formatMap(options.treePrefill);
|
|
112
|
+
if (treePrefill) {
|
|
113
|
+
args.push('--tree-prefill', treePrefill);
|
|
114
|
+
}
|
|
115
|
+
const mapSizes = formatMap(options.mapSizes);
|
|
116
|
+
if (mapSizes) {
|
|
117
|
+
args.push('--map-sizes', mapSizes);
|
|
118
|
+
}
|
|
119
|
+
args.push('--initial-header-generator-point', options.initialHeaderGeneratorPoint.toString());
|
|
120
|
+
if (genesis.prefilledPublicData.length > 0) {
|
|
121
|
+
const pairs = genesis.prefilledPublicData.map((data)=>[
|
|
122
|
+
data.slot.toBuffer().toString('hex'),
|
|
123
|
+
data.value.toBuffer().toString('hex')
|
|
124
|
+
]);
|
|
125
|
+
args.push('--prefilled-public-data', JSON.stringify(pairs));
|
|
126
|
+
}
|
|
127
|
+
const genesisTimestamp = Number(genesis.genesisTimestamp);
|
|
128
|
+
if (genesisTimestamp !== 0) {
|
|
129
|
+
args.push('--genesis-timestamp', genesisTimestamp.toString());
|
|
130
|
+
}
|
|
131
|
+
return args;
|
|
132
|
+
}
|
|
133
|
+
// ————— Response conversion helpers —————
|
|
134
|
+
/** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
|
|
135
|
+
if (obj instanceof Uint8Array) {
|
|
136
|
+
return Buffer.from(obj);
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(obj)) {
|
|
139
|
+
return obj.map(convertUint8ArraysToBuffers);
|
|
140
|
+
}
|
|
141
|
+
if (obj !== null && typeof obj === 'object') {
|
|
142
|
+
const result = {};
|
|
143
|
+
for (const [key, value] of Object.entries(obj)){
|
|
144
|
+
result[key] = convertUint8ArraysToBuffers(value);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
return obj;
|
|
149
|
+
}
|
|
150
|
+
/** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
|
|
151
|
+
const result = {};
|
|
152
|
+
for (const { treeId, root, size } of state){
|
|
153
|
+
result[treeId] = [
|
|
154
|
+
Buffer.from(root),
|
|
155
|
+
BigInt(size)
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/** Convert Wsdb WorldStateStatusSummary to the native world-state format. */ function convertStatusSummary(s) {
|
|
161
|
+
return {
|
|
162
|
+
unfinalizedBlockNumber: s.unfinalizedBlockNumber,
|
|
163
|
+
finalizedBlockNumber: s.finalizedBlockNumber,
|
|
164
|
+
oldestHistoricalBlock: s.oldestHistoricalBlock,
|
|
165
|
+
treesAreSynched: s.treesAreSynched
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function convertDBStats(s) {
|
|
169
|
+
return {
|
|
170
|
+
name: s.name,
|
|
171
|
+
numDataItems: s.numDataItems,
|
|
172
|
+
totalUsedSize: s.totalUsedSize
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function convertTreeDBStats(s) {
|
|
176
|
+
return {
|
|
177
|
+
mapSize: s.mapSize,
|
|
178
|
+
physicalFileSize: s.physicalFileSize,
|
|
179
|
+
blocksDBStats: convertDBStats(s.blocksDBStats),
|
|
180
|
+
nodesDBStats: convertDBStats(s.nodesDBStats),
|
|
181
|
+
leafPreimagesDBStats: convertDBStats(s.leafPreimagesDBStats),
|
|
182
|
+
leafIndicesDBStats: convertDBStats(s.leafIndicesDBStats),
|
|
183
|
+
blockIndicesDBStats: convertDBStats(s.blockIndicesDBStats)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function convertWorldStateDBStats(s) {
|
|
187
|
+
return {
|
|
188
|
+
noteHashTreeStats: convertTreeDBStats(s.noteHashTreeStats),
|
|
189
|
+
messageTreeStats: convertTreeDBStats(s.messageTreeStats),
|
|
190
|
+
archiveTreeStats: convertTreeDBStats(s.archiveTreeStats),
|
|
191
|
+
publicDataTreeStats: convertTreeDBStats(s.publicDataTreeStats),
|
|
192
|
+
nullifierTreeStats: convertTreeDBStats(s.nullifierTreeStats)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function convertTreeMeta(m) {
|
|
196
|
+
return {
|
|
197
|
+
name: m.name,
|
|
198
|
+
depth: m.depth,
|
|
199
|
+
size: m.size,
|
|
200
|
+
committedSize: m.committedSize,
|
|
201
|
+
root: m.root,
|
|
202
|
+
initialSize: m.initialSize,
|
|
203
|
+
initialRoot: m.initialRoot,
|
|
204
|
+
oldestHistoricBlock: m.oldestHistoricBlock,
|
|
205
|
+
unfinalizedBlockHeight: m.unfinalizedBlockHeight,
|
|
206
|
+
finalizedBlockHeight: m.finalizedBlockHeight
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function convertWorldStateMeta(m) {
|
|
210
|
+
return {
|
|
211
|
+
noteHashTreeMeta: convertTreeMeta(m.noteHashTreeMeta),
|
|
212
|
+
messageTreeMeta: convertTreeMeta(m.messageTreeMeta),
|
|
213
|
+
archiveTreeMeta: convertTreeMeta(m.archiveTreeMeta),
|
|
214
|
+
publicDataTreeMeta: convertTreeMeta(m.publicDataTreeMeta),
|
|
215
|
+
nullifierTreeMeta: convertTreeMeta(m.nullifierTreeMeta)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function convertStatusFull(s) {
|
|
219
|
+
return {
|
|
220
|
+
summary: convertStatusSummary(s.summary),
|
|
221
|
+
dbStats: convertWorldStateDBStats(s.dbStats),
|
|
222
|
+
meta: convertWorldStateMeta(s.meta)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/** Convert Wsdb SiblingPathAndIndex to NAPI format. */ function convertSiblingPathAndIndex(s) {
|
|
226
|
+
if (!s) {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
index: BigInt(s.index),
|
|
231
|
+
path: s.path.map((p)=>Buffer.from(p))
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// ————— Public API —————
|
|
235
|
+
/**
|
|
236
|
+
* IPC-backed world state instance.
|
|
237
|
+
* Uses WsdbService (spawns aztec-wsdb binary) and the generated AsyncApi
|
|
238
|
+
* to communicate via the NamedUnion IPC protocol.
|
|
239
|
+
*/ export class IpcWorldState {
|
|
240
|
+
wsdb;
|
|
241
|
+
instrumentation;
|
|
242
|
+
log;
|
|
243
|
+
open;
|
|
244
|
+
queues;
|
|
245
|
+
api;
|
|
246
|
+
/** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
|
|
247
|
+
constructor(wsdb, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
|
|
248
|
+
this.wsdb = wsdb;
|
|
249
|
+
this.instrumentation = instrumentation;
|
|
250
|
+
this.log = log;
|
|
251
|
+
this.open = true;
|
|
252
|
+
this.queues = new Map();
|
|
253
|
+
this.checkpointDepths = new Map();
|
|
254
|
+
this.api = wsdb;
|
|
255
|
+
this.queues.set(0, new WorldStateOpsQueue());
|
|
256
|
+
this.log.info('Created IPC-backed world state instance');
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Spawn an `aztec-wsdb` subprocess and return an IPC-backed world state wrapping it.
|
|
260
|
+
* Encapsulates wsdb binary discovery, service construction, and readiness wait.
|
|
261
|
+
*/ static async spawn(dataDir, wsTreeMapSizes, genesis, instrumentation, bindings) {
|
|
262
|
+
const threads = getWsdbThreadCount();
|
|
263
|
+
const wsdb = await WsdbService.spawn({
|
|
264
|
+
transport: 'uds',
|
|
265
|
+
extraArgs: getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads),
|
|
266
|
+
env: {
|
|
267
|
+
HARDWARE_CONCURRENCY: threads.toString()
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
return new IpcWorldState(wsdb, instrumentation, bindings);
|
|
271
|
+
}
|
|
272
|
+
/** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
|
|
273
|
+
return this.wsdb.getIpcPath();
|
|
274
|
+
}
|
|
275
|
+
async call(messageType, body, responseHandler = (response)=>response, errorHandler = (_)=>{}) {
|
|
276
|
+
let forkId = -1;
|
|
277
|
+
let committedOnly = false;
|
|
278
|
+
if (isWithCanonical(body)) {
|
|
279
|
+
forkId = 0;
|
|
280
|
+
} else if (isWithForkId(body)) {
|
|
281
|
+
forkId = body.forkId;
|
|
282
|
+
} else if (isWithRevision(body)) {
|
|
283
|
+
forkId = body.revision.forkId;
|
|
284
|
+
committedOnly = body.revision.includeUncommitted === false;
|
|
285
|
+
} else {
|
|
286
|
+
const _ = body;
|
|
287
|
+
throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
|
|
288
|
+
}
|
|
289
|
+
let requestQueue = this.queues.get(forkId);
|
|
290
|
+
if (requestQueue === undefined) {
|
|
291
|
+
requestQueue = new WorldStateOpsQueue();
|
|
292
|
+
this.queues.set(forkId, requestQueue);
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
return await requestQueue.execute(async ()=>{
|
|
296
|
+
assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
|
|
297
|
+
assert.equal(this.open, true, 'IPC instance is closed');
|
|
298
|
+
let response;
|
|
299
|
+
try {
|
|
300
|
+
response = await this._sendMessage(messageType, body);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
errorHandler(error.message);
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
return responseHandler(response);
|
|
306
|
+
}, messageType, committedOnly);
|
|
307
|
+
} finally{
|
|
308
|
+
if (messageType === WorldStateMessageType.DELETE_FORK) {
|
|
309
|
+
await requestQueue.stop();
|
|
310
|
+
this.queues.delete(forkId);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async close() {
|
|
315
|
+
if (!this.open) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
this.open = false;
|
|
319
|
+
const queue = this.queues.get(0);
|
|
320
|
+
await queue.stop();
|
|
321
|
+
await this.wsdb.destroy();
|
|
322
|
+
}
|
|
323
|
+
async _sendMessage(messageType, body) {
|
|
324
|
+
const start = performance.now();
|
|
325
|
+
try {
|
|
326
|
+
const response = await this.dispatch(messageType, body);
|
|
327
|
+
const durationMs = performance.now() - start;
|
|
328
|
+
this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
|
|
329
|
+
duration: durationMs
|
|
330
|
+
});
|
|
331
|
+
this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
|
|
332
|
+
return response;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async dispatch(messageType, body) {
|
|
339
|
+
switch(messageType){
|
|
340
|
+
// ——— Tree info & state reference ———
|
|
341
|
+
case WorldStateMessageType.GET_TREE_INFO:
|
|
342
|
+
{
|
|
343
|
+
const b = body;
|
|
344
|
+
const resp = await this.api.getTreeInfo({
|
|
345
|
+
treeId: b.treeId,
|
|
346
|
+
revision: toWsdbRevision(b.revision)
|
|
347
|
+
});
|
|
348
|
+
return {
|
|
349
|
+
treeId: resp.treeId,
|
|
350
|
+
root: Buffer.from(resp.root),
|
|
351
|
+
size: resp.size,
|
|
352
|
+
depth: resp.depth
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
case WorldStateMessageType.GET_STATE_REFERENCE:
|
|
356
|
+
{
|
|
357
|
+
const b = body;
|
|
358
|
+
const resp = await this.api.getStateReference({
|
|
359
|
+
revision: toWsdbRevision(b.revision)
|
|
360
|
+
});
|
|
361
|
+
return {
|
|
362
|
+
state: convertStateRef(resp.state)
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
|
|
366
|
+
{
|
|
367
|
+
const resp = await this.api.getInitialStateReference({});
|
|
368
|
+
return {
|
|
369
|
+
state: convertStateRef(resp.state)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
// ——— Leaf queries ———
|
|
373
|
+
case WorldStateMessageType.GET_LEAF_VALUE:
|
|
374
|
+
{
|
|
375
|
+
const b = body;
|
|
376
|
+
const revision = toWsdbRevision(b.revision);
|
|
377
|
+
const leafIndex = Number(b.leafIndex);
|
|
378
|
+
if (b.treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
|
|
379
|
+
const resp = await this.api.getPublicDataLeafValue({
|
|
380
|
+
revision,
|
|
381
|
+
leafIndex
|
|
382
|
+
});
|
|
383
|
+
return resp.value ? fromPublicDataLeaf(resp.value) : undefined;
|
|
384
|
+
}
|
|
385
|
+
if (b.treeId === MerkleTreeId.NULLIFIER_TREE) {
|
|
386
|
+
const resp = await this.api.getNullifierLeafValue({
|
|
387
|
+
revision,
|
|
388
|
+
leafIndex
|
|
389
|
+
});
|
|
390
|
+
return resp.value ? fromNullifierLeaf(resp.value) : undefined;
|
|
391
|
+
}
|
|
392
|
+
const resp = await this.api.getLeafValue({
|
|
393
|
+
treeId: b.treeId,
|
|
394
|
+
revision,
|
|
395
|
+
leafIndex
|
|
396
|
+
});
|
|
397
|
+
if (!resp.value) {
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
return Buffer.from(resp.value);
|
|
401
|
+
}
|
|
402
|
+
case WorldStateMessageType.GET_LEAF_PREIMAGE:
|
|
403
|
+
{
|
|
404
|
+
const b = body;
|
|
405
|
+
const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.getPublicDataLeafPreimage({
|
|
406
|
+
revision: toWsdbRevision(b.revision),
|
|
407
|
+
leafIndex: Number(b.leafIndex)
|
|
408
|
+
}) : await this.api.getNullifierLeafPreimage({
|
|
409
|
+
revision: toWsdbRevision(b.revision),
|
|
410
|
+
leafIndex: Number(b.leafIndex)
|
|
411
|
+
});
|
|
412
|
+
if (!resp.preimage) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
return convertUint8ArraysToBuffers(resp.preimage);
|
|
416
|
+
}
|
|
417
|
+
case WorldStateMessageType.GET_SIBLING_PATH:
|
|
418
|
+
{
|
|
419
|
+
const b = body;
|
|
420
|
+
const resp = await this.api.getSiblingPath({
|
|
421
|
+
treeId: b.treeId,
|
|
422
|
+
revision: toWsdbRevision(b.revision),
|
|
423
|
+
leafIndex: Number(b.leafIndex)
|
|
424
|
+
});
|
|
425
|
+
return resp.path.map((p)=>Buffer.from(p));
|
|
426
|
+
}
|
|
427
|
+
case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
|
|
428
|
+
{
|
|
429
|
+
const b = body;
|
|
430
|
+
const resp = await this.api.getBlockNumbersForLeafIndices({
|
|
431
|
+
treeId: b.treeId,
|
|
432
|
+
revision: toWsdbRevision(b.revision),
|
|
433
|
+
leafIndices: b.leafIndices.map(Number)
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
blockNumbers: resp.blockNumbers.map((n)=>n != null ? BigInt(n) : undefined)
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
// ——— Find operations ———
|
|
440
|
+
case WorldStateMessageType.FIND_LEAF_INDICES:
|
|
441
|
+
{
|
|
442
|
+
const b = body;
|
|
443
|
+
const revision = toWsdbRevision(b.revision);
|
|
444
|
+
const startIndex = Number(b.startIndex);
|
|
445
|
+
const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataLeafIndices({
|
|
446
|
+
revision,
|
|
447
|
+
leaves: b.leaves.map(toPublicDataLeaf),
|
|
448
|
+
startIndex
|
|
449
|
+
}) : b.treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierLeafIndices({
|
|
450
|
+
revision,
|
|
451
|
+
leaves: b.leaves.map(toNullifierLeaf),
|
|
452
|
+
startIndex
|
|
453
|
+
}) : await this.api.findLeafIndices({
|
|
454
|
+
treeId: b.treeId,
|
|
455
|
+
revision,
|
|
456
|
+
leaves: b.leaves.map(toFrLeaf),
|
|
457
|
+
startIndex
|
|
458
|
+
});
|
|
459
|
+
return {
|
|
460
|
+
indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
case WorldStateMessageType.FIND_LOW_LEAF:
|
|
464
|
+
{
|
|
465
|
+
const b = body;
|
|
466
|
+
const resp = await this.api.findLowLeaf({
|
|
467
|
+
treeId: b.treeId,
|
|
468
|
+
revision: toWsdbRevision(b.revision),
|
|
469
|
+
key: new Uint8Array(b.key.toBuffer())
|
|
470
|
+
});
|
|
471
|
+
return {
|
|
472
|
+
alreadyPresent: resp.alreadyPresent,
|
|
473
|
+
index: BigInt(resp.index)
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
case WorldStateMessageType.FIND_SIBLING_PATHS:
|
|
477
|
+
{
|
|
478
|
+
const b = body;
|
|
479
|
+
const revision = toWsdbRevision(b.revision);
|
|
480
|
+
const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataSiblingPaths({
|
|
481
|
+
revision,
|
|
482
|
+
leaves: b.leaves.map(toPublicDataLeaf)
|
|
483
|
+
}) : b.treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierSiblingPaths({
|
|
484
|
+
revision,
|
|
485
|
+
leaves: b.leaves.map(toNullifierLeaf)
|
|
486
|
+
}) : await this.api.findSiblingPaths({
|
|
487
|
+
treeId: b.treeId,
|
|
488
|
+
revision,
|
|
489
|
+
leaves: b.leaves.map(toFrLeaf)
|
|
490
|
+
});
|
|
491
|
+
return {
|
|
492
|
+
paths: resp.paths.map(convertSiblingPathAndIndex)
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
// ——— Mutations ———
|
|
496
|
+
case WorldStateMessageType.APPEND_LEAVES:
|
|
497
|
+
{
|
|
498
|
+
const b = body;
|
|
499
|
+
if (b.treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
|
|
500
|
+
await this.api.appendPublicDataLeaves({
|
|
501
|
+
leaves: b.leaves.map(toPublicDataLeaf),
|
|
502
|
+
forkId: b.forkId
|
|
503
|
+
});
|
|
504
|
+
} else if (b.treeId === MerkleTreeId.NULLIFIER_TREE) {
|
|
505
|
+
await this.api.appendNullifierLeaves({
|
|
506
|
+
leaves: b.leaves.map(toNullifierLeaf),
|
|
507
|
+
forkId: b.forkId
|
|
508
|
+
});
|
|
509
|
+
} else {
|
|
510
|
+
await this.api.appendLeaves({
|
|
511
|
+
treeId: b.treeId,
|
|
512
|
+
leaves: b.leaves.map(toFrLeaf),
|
|
513
|
+
forkId: b.forkId
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
return undefined;
|
|
517
|
+
}
|
|
518
|
+
case WorldStateMessageType.BATCH_INSERT:
|
|
519
|
+
{
|
|
520
|
+
const b = body;
|
|
521
|
+
const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.batchInsertPublicData({
|
|
522
|
+
leaves: b.leaves.map(toPublicDataLeaf),
|
|
523
|
+
subtreeDepth: b.subtreeDepth,
|
|
524
|
+
forkId: b.forkId
|
|
525
|
+
}) : await this.api.batchInsertNullifier({
|
|
526
|
+
leaves: b.leaves.map(toNullifierLeaf),
|
|
527
|
+
subtreeDepth: b.subtreeDepth,
|
|
528
|
+
forkId: b.forkId
|
|
529
|
+
});
|
|
530
|
+
return b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromBatchInsertionResult(resp.result, fromPublicDataLeaf) : fromBatchInsertionResult(resp.result, fromNullifierLeaf);
|
|
531
|
+
}
|
|
532
|
+
case WorldStateMessageType.SEQUENTIAL_INSERT:
|
|
533
|
+
{
|
|
534
|
+
const b = body;
|
|
535
|
+
const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.sequentialInsertPublicData({
|
|
536
|
+
leaves: b.leaves.map(toPublicDataLeaf),
|
|
537
|
+
forkId: b.forkId
|
|
538
|
+
}) : await this.api.sequentialInsertNullifier({
|
|
539
|
+
leaves: b.leaves.map(toNullifierLeaf),
|
|
540
|
+
forkId: b.forkId
|
|
541
|
+
});
|
|
542
|
+
return b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromSequentialInsertionResult(resp.result, fromPublicDataLeaf) : fromSequentialInsertionResult(resp.result, fromNullifierLeaf);
|
|
543
|
+
}
|
|
544
|
+
case WorldStateMessageType.UPDATE_ARCHIVE:
|
|
545
|
+
{
|
|
546
|
+
const b = body;
|
|
547
|
+
await this.api.updateArchive({
|
|
548
|
+
blockStateRef: blockStateRefToMap(b.blockStateRef),
|
|
549
|
+
blockHeaderHash: new Uint8Array(b.blockHeaderHash),
|
|
550
|
+
forkId: b.forkId
|
|
551
|
+
});
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
// ——— Commit / Rollback ———
|
|
555
|
+
case WorldStateMessageType.COMMIT:
|
|
556
|
+
{
|
|
557
|
+
await this.api.commit({});
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
case WorldStateMessageType.ROLLBACK:
|
|
561
|
+
{
|
|
562
|
+
await this.api.rollback({});
|
|
563
|
+
return undefined;
|
|
564
|
+
}
|
|
565
|
+
// ——— Block sync ———
|
|
566
|
+
case WorldStateMessageType.SYNC_BLOCK:
|
|
567
|
+
{
|
|
568
|
+
const b = body;
|
|
569
|
+
const resp = await this.api.syncBlock({
|
|
570
|
+
blockNumber: Number(b.blockNumber),
|
|
571
|
+
blockStateRef: blockStateRefToMap(b.blockStateRef),
|
|
572
|
+
blockHeaderHash: new Uint8Array(b.blockHeaderHash),
|
|
573
|
+
paddedNoteHashes: b.paddedNoteHashes.map(toFrLeaf),
|
|
574
|
+
paddedL1ToL2Messages: b.paddedL1ToL2Messages.map(toFrLeaf),
|
|
575
|
+
paddedNullifiers: b.paddedNullifiers.map(toNullifierLeaf),
|
|
576
|
+
publicDataWrites: b.publicDataWrites.map(toPublicDataLeaf)
|
|
577
|
+
});
|
|
578
|
+
return convertStatusFull(resp.status);
|
|
579
|
+
}
|
|
580
|
+
// ——— Fork management ———
|
|
581
|
+
case WorldStateMessageType.CREATE_FORK:
|
|
582
|
+
{
|
|
583
|
+
const b = body;
|
|
584
|
+
const resp = await this.api.createFork({
|
|
585
|
+
latest: b.latest,
|
|
586
|
+
blockNumber: Number(b.blockNumber)
|
|
587
|
+
});
|
|
588
|
+
return {
|
|
589
|
+
forkId: resp.forkId
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
case WorldStateMessageType.DELETE_FORK:
|
|
593
|
+
{
|
|
594
|
+
const b = body;
|
|
595
|
+
await this.api.deleteFork({
|
|
596
|
+
forkId: b.forkId
|
|
597
|
+
});
|
|
598
|
+
return undefined;
|
|
599
|
+
}
|
|
600
|
+
// ——— Block finalization ———
|
|
601
|
+
case WorldStateMessageType.FINALIZE_BLOCKS:
|
|
602
|
+
{
|
|
603
|
+
const b = body;
|
|
604
|
+
const resp = await this.api.finalizeBlocks({
|
|
605
|
+
toBlockNumber: Number(b.toBlockNumber)
|
|
606
|
+
});
|
|
607
|
+
return convertStatusSummary(resp.status);
|
|
608
|
+
}
|
|
609
|
+
case WorldStateMessageType.UNWIND_BLOCKS:
|
|
610
|
+
{
|
|
611
|
+
const b = body;
|
|
612
|
+
const resp = await this.api.unwindBlocks({
|
|
613
|
+
toBlockNumber: Number(b.toBlockNumber)
|
|
614
|
+
});
|
|
615
|
+
return convertStatusFull(resp.status);
|
|
616
|
+
}
|
|
617
|
+
case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
|
|
618
|
+
{
|
|
619
|
+
const b = body;
|
|
620
|
+
const resp = await this.api.removeHistoricalBlocks({
|
|
621
|
+
toBlockNumber: Number(b.toBlockNumber)
|
|
622
|
+
});
|
|
623
|
+
return convertStatusFull(resp.status);
|
|
624
|
+
}
|
|
625
|
+
// ——— Status ———
|
|
626
|
+
case WorldStateMessageType.GET_STATUS:
|
|
627
|
+
{
|
|
628
|
+
const resp = await this.api.getStatus({});
|
|
629
|
+
return convertStatusSummary(resp.status);
|
|
630
|
+
}
|
|
631
|
+
// ——— Checkpoints ———
|
|
632
|
+
case WorldStateMessageType.CREATE_CHECKPOINT:
|
|
633
|
+
{
|
|
634
|
+
const b = body;
|
|
635
|
+
await this.api.createCheckpoint({
|
|
636
|
+
forkId: b.forkId
|
|
637
|
+
});
|
|
638
|
+
const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
|
|
639
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
640
|
+
return {
|
|
641
|
+
depth
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
case WorldStateMessageType.COMMIT_CHECKPOINT:
|
|
645
|
+
{
|
|
646
|
+
const b = body;
|
|
647
|
+
await this.api.commitCheckpoint({
|
|
648
|
+
forkId: b.forkId
|
|
649
|
+
});
|
|
650
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
651
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
652
|
+
return undefined;
|
|
653
|
+
}
|
|
654
|
+
case WorldStateMessageType.REVERT_CHECKPOINT:
|
|
655
|
+
{
|
|
656
|
+
const b = body;
|
|
657
|
+
await this.api.revertCheckpoint({
|
|
658
|
+
forkId: b.forkId
|
|
659
|
+
});
|
|
660
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
661
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
662
|
+
return undefined;
|
|
663
|
+
}
|
|
664
|
+
case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
|
|
665
|
+
{
|
|
666
|
+
const b = body;
|
|
667
|
+
const targetDepth = b.depth ?? 0;
|
|
668
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
669
|
+
if (targetDepth === 0) {
|
|
670
|
+
// Commit everything — use the bulk operation
|
|
671
|
+
await this.api.commitAllCheckpoints({
|
|
672
|
+
forkId: b.forkId
|
|
673
|
+
});
|
|
674
|
+
} else {
|
|
675
|
+
// Commit one level at a time down to target depth
|
|
676
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
677
|
+
await this.api.commitCheckpoint({
|
|
678
|
+
forkId: b.forkId
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
683
|
+
return undefined;
|
|
684
|
+
}
|
|
685
|
+
case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
|
|
686
|
+
{
|
|
687
|
+
const b = body;
|
|
688
|
+
const targetDepth = b.depth ?? 0;
|
|
689
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
690
|
+
if (targetDepth === 0) {
|
|
691
|
+
// Revert everything — use the bulk operation
|
|
692
|
+
await this.api.revertAllCheckpoints({
|
|
693
|
+
forkId: b.forkId
|
|
694
|
+
});
|
|
695
|
+
} else {
|
|
696
|
+
// Revert one level at a time down to target depth
|
|
697
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
698
|
+
await this.api.revertCheckpoint({
|
|
699
|
+
forkId: b.forkId
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
// ——— Misc ———
|
|
707
|
+
case WorldStateMessageType.COPY_STORES:
|
|
708
|
+
{
|
|
709
|
+
const b = body;
|
|
710
|
+
await this.api.copyStores({
|
|
711
|
+
dstPath: b.dstPath,
|
|
712
|
+
compact: b.compact
|
|
713
|
+
});
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
case WorldStateMessageType.CLOSE:
|
|
717
|
+
{
|
|
718
|
+
return undefined;
|
|
719
|
+
}
|
|
720
|
+
default:
|
|
721
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Helper to create WsdbOptions from standard world state config.
|
|
727
|
+
* Returns the options needed to construct a wsdb service command line.
|
|
728
|
+
*/ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
|
|
729
|
+
return {
|
|
730
|
+
treeHeights: {
|
|
731
|
+
[MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
|
|
732
|
+
[MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
|
|
733
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
|
|
734
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
|
|
735
|
+
[MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
|
|
736
|
+
},
|
|
737
|
+
treePrefill: {
|
|
738
|
+
[MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
|
|
739
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
|
|
740
|
+
},
|
|
741
|
+
mapSizes: {
|
|
742
|
+
[MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
|
|
743
|
+
[MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
|
|
744
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
|
|
745
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
|
|
746
|
+
[MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb
|
|
747
|
+
},
|
|
748
|
+
initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH
|
|
749
|
+
};
|
|
750
|
+
}
|