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