@aztec/world-state 0.0.1-commit.2f68f620 → 0.0.1-commit.3100065
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 +3 -4
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +9 -4
- package/dest/native/ipc_world_state_instance.d.ts +22 -59
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -1
- package/dest/native/ipc_world_state_instance.js +495 -520
- package/dest/native/merkle_trees_facade.d.ts +3 -4
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +109 -37
- package/dest/native/message.d.ts +233 -14
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +42 -0
- package/dest/native/native_world_state.d.ts +28 -14
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +102 -101
- package/dest/native/native_world_state_instance.d.ts +42 -58
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +222 -5
- package/dest/native/world_state_ops_queue.d.ts +19 -0
- package/dest/native/world_state_ops_queue.d.ts.map +1 -0
- package/dest/native/world_state_ops_queue.js +146 -0
- 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 +1 -1
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +1 -1
- 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 +11 -6
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +55 -43
- 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 +2 -2
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +15 -4
- package/package.json +10 -12
- package/src/instrumentation/instrumentation.ts +18 -6
- package/src/native/ipc_world_state_instance.ts +503 -608
- package/src/native/merkle_trees_facade.ts +104 -59
- package/src/native/message.ts +300 -13
- package/src/native/native_world_state.ts +156 -121
- package/src/native/native_world_state_instance.ts +307 -96
- package/src/native/world_state_ops_queue.ts +190 -0
- package/src/synchronizer/errors.ts +8 -0
- package/src/synchronizer/factory.ts +1 -0
- package/src/synchronizer/index.ts +1 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +63 -30
- package/src/test/utils.ts +10 -1
- package/src/testing.ts +14 -4
- package/dest/native/world_state_operation.d.ts +0 -7
- package/dest/native/world_state_operation.d.ts.map +0 -1
- package/dest/native/world_state_operation.js +0 -5
- package/src/native/world_state_operation.ts +0 -33
|
@@ -1,139 +1,38 @@
|
|
|
1
|
+
import { AsyncApi } from '@aztec/bb.js/aztec-wsdb';
|
|
1
2
|
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
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
import { Decoder, Encoder } from 'msgpackr';
|
|
7
|
+
import { WorldStateMessageType, isWithCanonical, isWithForkId, isWithRevision } from './message.js';
|
|
8
|
+
import { WorldStateOpsQueue } from './world_state_ops_queue.js';
|
|
9
|
+
// ————— Msgpack helpers —————
|
|
10
|
+
const msgpackEncoder = new Encoder({
|
|
11
|
+
useRecords: false
|
|
12
|
+
});
|
|
13
|
+
const msgpackDecoder = new Decoder({
|
|
14
|
+
useRecords: false
|
|
15
|
+
});
|
|
16
|
+
/** Msgpack-encode a SerializedLeafValue into bytes for IPC transport. */ function serializeLeafToBytes(leaf) {
|
|
17
|
+
return Buffer.from(msgpackEncoder.pack(leaf));
|
|
18
|
+
}
|
|
6
19
|
// ————— Request conversion helpers —————
|
|
7
20
|
function toWsdbRevision(rev) {
|
|
8
21
|
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
forkid: rev.forkId,
|
|
23
|
+
blocknumber: Number(rev.blockNumber),
|
|
24
|
+
includeuncommitted: rev.includeUncommitted
|
|
12
25
|
};
|
|
13
26
|
}
|
|
14
27
|
function blockStateRefToMap(ref) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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');
|
|
28
|
+
const result = new Map();
|
|
29
|
+
for (const [treeId, [root, size]] of ref.entries()){
|
|
30
|
+
result.set(treeId, [
|
|
31
|
+
new Uint8Array(root),
|
|
32
|
+
Number(size)
|
|
33
|
+
]);
|
|
84
34
|
}
|
|
85
|
-
return
|
|
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;
|
|
35
|
+
return result;
|
|
137
36
|
}
|
|
138
37
|
// ————— Response conversion helpers —————
|
|
139
38
|
/** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
|
|
@@ -152,49 +51,57 @@ function getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads) {
|
|
|
152
51
|
}
|
|
153
52
|
return obj;
|
|
154
53
|
}
|
|
54
|
+
/** Decode a msgpack-encoded leaf value blob and convert Uint8Arrays to Buffers. */ function decodeLeafValue(encoded) {
|
|
55
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
|
|
56
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
57
|
+
}
|
|
58
|
+
/** Decode a msgpack-encoded indexed leaf preimage blob. */ function decodeLeafPreimage(encoded) {
|
|
59
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
|
|
60
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
61
|
+
}
|
|
155
62
|
/** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
|
|
156
63
|
const result = {};
|
|
157
|
-
for (const
|
|
158
|
-
result[
|
|
64
|
+
for (const [key, [root, size]] of Object.entries(state)){
|
|
65
|
+
result[Number(key)] = [
|
|
159
66
|
Buffer.from(root),
|
|
160
67
|
BigInt(size)
|
|
161
68
|
];
|
|
162
69
|
}
|
|
163
70
|
return result;
|
|
164
71
|
}
|
|
165
|
-
/** Convert Wsdb WorldStateStatusSummary to
|
|
72
|
+
/** Convert Wsdb WorldStateStatusSummary (lowercase) to NAPI format (camelCase). */ function convertStatusSummary(s) {
|
|
166
73
|
return {
|
|
167
|
-
unfinalizedBlockNumber: s.
|
|
168
|
-
finalizedBlockNumber: s.
|
|
169
|
-
oldestHistoricalBlock: s.
|
|
170
|
-
treesAreSynched: s.
|
|
74
|
+
unfinalizedBlockNumber: s.unfinalizedblocknumber,
|
|
75
|
+
finalizedBlockNumber: s.finalizedblocknumber,
|
|
76
|
+
oldestHistoricalBlock: s.oldesthistoricalblock,
|
|
77
|
+
treesAreSynched: s.treesaresynched
|
|
171
78
|
};
|
|
172
79
|
}
|
|
173
80
|
function convertDBStats(s) {
|
|
174
81
|
return {
|
|
175
82
|
name: s.name,
|
|
176
|
-
numDataItems: s.
|
|
177
|
-
totalUsedSize: s.
|
|
83
|
+
numDataItems: s.numdataitems,
|
|
84
|
+
totalUsedSize: s.totalusedsize
|
|
178
85
|
};
|
|
179
86
|
}
|
|
180
87
|
function convertTreeDBStats(s) {
|
|
181
88
|
return {
|
|
182
|
-
mapSize: s.
|
|
183
|
-
physicalFileSize: s.
|
|
184
|
-
blocksDBStats: convertDBStats(s.
|
|
185
|
-
nodesDBStats: convertDBStats(s.
|
|
186
|
-
leafPreimagesDBStats: convertDBStats(s.
|
|
187
|
-
leafIndicesDBStats: convertDBStats(s.
|
|
188
|
-
blockIndicesDBStats: convertDBStats(s.
|
|
89
|
+
mapSize: s.mapsize,
|
|
90
|
+
physicalFileSize: s.physicalfilesize,
|
|
91
|
+
blocksDBStats: convertDBStats(s.blocksdbstats),
|
|
92
|
+
nodesDBStats: convertDBStats(s.nodesdbstats),
|
|
93
|
+
leafPreimagesDBStats: convertDBStats(s.leafpreimagesdbstats),
|
|
94
|
+
leafIndicesDBStats: convertDBStats(s.leafindicesdbstats),
|
|
95
|
+
blockIndicesDBStats: convertDBStats(s.blockindicesdbstats)
|
|
189
96
|
};
|
|
190
97
|
}
|
|
191
98
|
function convertWorldStateDBStats(s) {
|
|
192
99
|
return {
|
|
193
|
-
noteHashTreeStats: convertTreeDBStats(s.
|
|
194
|
-
messageTreeStats: convertTreeDBStats(s.
|
|
195
|
-
archiveTreeStats: convertTreeDBStats(s.
|
|
196
|
-
publicDataTreeStats: convertTreeDBStats(s.
|
|
197
|
-
nullifierTreeStats: convertTreeDBStats(s.
|
|
100
|
+
noteHashTreeStats: convertTreeDBStats(s.notehashtreestats),
|
|
101
|
+
messageTreeStats: convertTreeDBStats(s.messagetreestats),
|
|
102
|
+
archiveTreeStats: convertTreeDBStats(s.archivetreestats),
|
|
103
|
+
publicDataTreeStats: convertTreeDBStats(s.publicdatatreestats),
|
|
104
|
+
nullifierTreeStats: convertTreeDBStats(s.nullifiertreestats)
|
|
198
105
|
};
|
|
199
106
|
}
|
|
200
107
|
function convertTreeMeta(m) {
|
|
@@ -202,28 +109,28 @@ function convertTreeMeta(m) {
|
|
|
202
109
|
name: m.name,
|
|
203
110
|
depth: m.depth,
|
|
204
111
|
size: m.size,
|
|
205
|
-
committedSize: m.
|
|
112
|
+
committedSize: m.committedsize,
|
|
206
113
|
root: m.root,
|
|
207
|
-
initialSize: m.
|
|
208
|
-
initialRoot: m.
|
|
209
|
-
oldestHistoricBlock: m.
|
|
210
|
-
unfinalizedBlockHeight: m.
|
|
211
|
-
finalizedBlockHeight: m.
|
|
114
|
+
initialSize: m.initialsize,
|
|
115
|
+
initialRoot: m.initialroot,
|
|
116
|
+
oldestHistoricBlock: m.oldesthistoricblock,
|
|
117
|
+
unfinalizedBlockHeight: m.unfinalizedblockheight,
|
|
118
|
+
finalizedBlockHeight: m.finalizedblockheight
|
|
212
119
|
};
|
|
213
120
|
}
|
|
214
121
|
function convertWorldStateMeta(m) {
|
|
215
122
|
return {
|
|
216
|
-
noteHashTreeMeta: convertTreeMeta(m.
|
|
217
|
-
messageTreeMeta: convertTreeMeta(m.
|
|
218
|
-
archiveTreeMeta: convertTreeMeta(m.
|
|
219
|
-
publicDataTreeMeta: convertTreeMeta(m.
|
|
220
|
-
nullifierTreeMeta: convertTreeMeta(m.
|
|
123
|
+
noteHashTreeMeta: convertTreeMeta(m.notehashtreemeta),
|
|
124
|
+
messageTreeMeta: convertTreeMeta(m.messagetreemeta),
|
|
125
|
+
archiveTreeMeta: convertTreeMeta(m.archivetreemeta),
|
|
126
|
+
publicDataTreeMeta: convertTreeMeta(m.publicdatatreemeta),
|
|
127
|
+
nullifierTreeMeta: convertTreeMeta(m.nullifiertreemeta)
|
|
221
128
|
};
|
|
222
129
|
}
|
|
223
130
|
function convertStatusFull(s) {
|
|
224
131
|
return {
|
|
225
132
|
summary: convertStatusSummary(s.summary),
|
|
226
|
-
dbStats: convertWorldStateDBStats(s.
|
|
133
|
+
dbStats: convertWorldStateDBStats(s.dbstats),
|
|
227
134
|
meta: convertWorldStateMeta(s.meta)
|
|
228
135
|
};
|
|
229
136
|
}
|
|
@@ -236,405 +143,473 @@ function convertStatusFull(s) {
|
|
|
236
143
|
path: s.path.map((p)=>Buffer.from(p))
|
|
237
144
|
};
|
|
238
145
|
}
|
|
239
|
-
// ————— Public API —————
|
|
240
146
|
/**
|
|
241
147
|
* IPC-backed world state instance.
|
|
242
|
-
* Uses
|
|
148
|
+
* Uses WsdbBackend (spawns aztec-wsdb binary) and the generated AsyncApi
|
|
243
149
|
* to communicate via the NamedUnion IPC protocol.
|
|
244
150
|
*/ export class IpcWorldState {
|
|
245
|
-
|
|
151
|
+
wsdbBackend;
|
|
246
152
|
instrumentation;
|
|
247
153
|
log;
|
|
248
154
|
open;
|
|
155
|
+
queues;
|
|
249
156
|
api;
|
|
250
157
|
/** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
|
|
251
|
-
constructor(
|
|
252
|
-
this.
|
|
158
|
+
constructor(wsdbBackend, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
|
|
159
|
+
this.wsdbBackend = wsdbBackend;
|
|
253
160
|
this.instrumentation = instrumentation;
|
|
254
161
|
this.log = log;
|
|
255
162
|
this.open = true;
|
|
163
|
+
this.queues = new Map();
|
|
256
164
|
this.checkpointDepths = new Map();
|
|
257
|
-
this.api =
|
|
165
|
+
this.api = new AsyncApi(wsdbBackend);
|
|
166
|
+
this.queues.set(0, new WorldStateOpsQueue());
|
|
258
167
|
this.log.info('Created IPC-backed world state instance');
|
|
259
168
|
}
|
|
169
|
+
/** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
|
|
170
|
+
return this.wsdbBackend.getSocketPath();
|
|
171
|
+
}
|
|
260
172
|
/**
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
173
|
+
* Required by `NativeWorldStateInstance` for compatibility with the in-process
|
|
174
|
+
* NAPI path. The IPC backend does not expose an in-process pointer; callers that
|
|
175
|
+
* need to reach the WSDB process must use {@link getSocketPath} instead.
|
|
176
|
+
*/ getHandle() {
|
|
177
|
+
throw new Error('IpcWorldState has no in-process handle; use getSocketPath() instead');
|
|
178
|
+
}
|
|
179
|
+
async call(messageType, body, responseHandler = (response)=>response, errorHandler = (_)=>{}) {
|
|
180
|
+
let forkId = -1;
|
|
181
|
+
let committedOnly = false;
|
|
182
|
+
if (isWithCanonical(body)) {
|
|
183
|
+
forkId = 0;
|
|
184
|
+
} else if (isWithForkId(body)) {
|
|
185
|
+
forkId = body.forkId;
|
|
186
|
+
} else if (isWithRevision(body)) {
|
|
187
|
+
forkId = body.revision.forkId;
|
|
188
|
+
committedOnly = body.revision.includeUncommitted === false;
|
|
189
|
+
} else {
|
|
190
|
+
const _ = body;
|
|
191
|
+
throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
|
|
192
|
+
}
|
|
193
|
+
let requestQueue = this.queues.get(forkId);
|
|
194
|
+
if (requestQueue === undefined) {
|
|
195
|
+
requestQueue = new WorldStateOpsQueue();
|
|
196
|
+
this.queues.set(forkId, requestQueue);
|
|
197
|
+
}
|
|
198
|
+
// The per-fork queue is cleaned up in `finally` even on error, so the JS-side queues map cannot outlive
|
|
199
|
+
// the native fork (e.g. when the native fork was already destroyed by an unwind/historical-prune and
|
|
200
|
+
// DELETE_FORK rejects with "Fork not found").
|
|
201
|
+
let shouldDeleteForkQueue = false;
|
|
202
|
+
try {
|
|
203
|
+
const response = await requestQueue.execute(async ()=>{
|
|
204
|
+
assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
|
|
205
|
+
assert.equal(this.open, true, 'IPC instance is closed');
|
|
206
|
+
let response;
|
|
207
|
+
try {
|
|
208
|
+
response = await this._sendMessage(messageType, body);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
errorHandler(error.message);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
return responseHandler(response);
|
|
214
|
+
}, messageType, committedOnly);
|
|
215
|
+
return response;
|
|
216
|
+
} catch (err) {
|
|
217
|
+
shouldDeleteForkQueue = forkId !== 0 && err?.message === 'Fork not found';
|
|
218
|
+
throw err;
|
|
219
|
+
} finally{
|
|
220
|
+
if (messageType === WorldStateMessageType.DELETE_FORK || shouldDeleteForkQueue) {
|
|
221
|
+
await requestQueue.stop();
|
|
222
|
+
this.queues.delete(forkId);
|
|
271
223
|
}
|
|
272
|
-
}
|
|
273
|
-
return new IpcWorldState(wsdb, instrumentation, bindings);
|
|
274
|
-
}
|
|
275
|
-
getIpcPath() {
|
|
276
|
-
return this.wsdb.getIpcPath();
|
|
224
|
+
}
|
|
277
225
|
}
|
|
278
226
|
async close() {
|
|
279
227
|
if (!this.open) {
|
|
280
228
|
return;
|
|
281
229
|
}
|
|
282
230
|
this.open = false;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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');
|
|
231
|
+
const queue = this.queues.get(0);
|
|
232
|
+
// Send shutdown command. Under normal operation, the WSDB process sends its
|
|
233
|
+
// response before exiting (via ShutdownRequested in ipc_server.hpp). The
|
|
234
|
+
// try/catch is defensive: if the process is killed externally (SIGKILL, OOM)
|
|
235
|
+
// before responding, the pending IPC callback would be rejected by the socket
|
|
236
|
+
// close handler. We proceed to destroy the backend regardless.
|
|
237
|
+
try {
|
|
238
|
+
await queue.execute(async ()=>{
|
|
239
|
+
await this.api.wsdbShutdown({});
|
|
240
|
+
}, WorldStateMessageType.CLOSE, false);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
this.log.debug(`wsdbShutdown completed with error: ${err.message}`);
|
|
243
|
+
}
|
|
244
|
+
await queue.stop();
|
|
245
|
+
if (this.wsdbBackend.destroy) {
|
|
246
|
+
await this.wsdbBackend.destroy();
|
|
616
247
|
}
|
|
617
|
-
return await this.send(operation, request);
|
|
618
248
|
}
|
|
619
|
-
async
|
|
249
|
+
async _sendMessage(messageType, body) {
|
|
620
250
|
const start = performance.now();
|
|
621
251
|
try {
|
|
622
|
-
const response = await
|
|
252
|
+
const response = await this.dispatch(messageType, body);
|
|
623
253
|
const durationMs = performance.now() - start;
|
|
624
|
-
this.log.trace(`Call ${
|
|
254
|
+
this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
|
|
625
255
|
duration: durationMs
|
|
626
256
|
});
|
|
627
|
-
this.instrumentation.recordRoundTrip(durationMs * 1000,
|
|
257
|
+
this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
|
|
628
258
|
return response;
|
|
629
259
|
} catch (error) {
|
|
630
|
-
this.log.error(`Call ${
|
|
260
|
+
this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
|
|
631
261
|
throw error;
|
|
632
262
|
}
|
|
633
263
|
}
|
|
264
|
+
async dispatch(messageType, body) {
|
|
265
|
+
switch(messageType){
|
|
266
|
+
// ——— Tree info & state reference ———
|
|
267
|
+
case WorldStateMessageType.GET_TREE_INFO:
|
|
268
|
+
{
|
|
269
|
+
const b = body;
|
|
270
|
+
const resp = await this.api.wsdbGetTreeInfo({
|
|
271
|
+
treeid: b.treeId,
|
|
272
|
+
revision: toWsdbRevision(b.revision)
|
|
273
|
+
});
|
|
274
|
+
return {
|
|
275
|
+
treeId: resp.treeid,
|
|
276
|
+
root: Buffer.from(resp.root),
|
|
277
|
+
size: resp.size,
|
|
278
|
+
depth: resp.depth
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
case WorldStateMessageType.GET_STATE_REFERENCE:
|
|
282
|
+
{
|
|
283
|
+
const b = body;
|
|
284
|
+
const resp = await this.api.wsdbGetStateReference({
|
|
285
|
+
revision: toWsdbRevision(b.revision)
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
state: convertStateRef(resp.state)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
|
|
292
|
+
{
|
|
293
|
+
const resp = await this.api.wsdbGetInitialStateReference({});
|
|
294
|
+
return {
|
|
295
|
+
state: convertStateRef(resp.state)
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
// ——— Leaf queries ———
|
|
299
|
+
case WorldStateMessageType.GET_LEAF_VALUE:
|
|
300
|
+
{
|
|
301
|
+
const b = body;
|
|
302
|
+
const resp = await this.api.wsdbGetLeafValue({
|
|
303
|
+
treeid: b.treeId,
|
|
304
|
+
revision: toWsdbRevision(b.revision),
|
|
305
|
+
leafindex: Number(b.leafIndex)
|
|
306
|
+
});
|
|
307
|
+
if (!resp.value) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
return decodeLeafValue(resp.value);
|
|
311
|
+
}
|
|
312
|
+
case WorldStateMessageType.GET_LEAF_PREIMAGE:
|
|
313
|
+
{
|
|
314
|
+
const b = body;
|
|
315
|
+
const resp = await this.api.wsdbGetLeafPreimage({
|
|
316
|
+
treeid: b.treeId,
|
|
317
|
+
revision: toWsdbRevision(b.revision),
|
|
318
|
+
leafindex: Number(b.leafIndex)
|
|
319
|
+
});
|
|
320
|
+
if (!resp.preimage) {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
return decodeLeafPreimage(resp.preimage);
|
|
324
|
+
}
|
|
325
|
+
case WorldStateMessageType.GET_SIBLING_PATH:
|
|
326
|
+
{
|
|
327
|
+
const b = body;
|
|
328
|
+
const resp = await this.api.wsdbGetSiblingPath({
|
|
329
|
+
treeid: b.treeId,
|
|
330
|
+
revision: toWsdbRevision(b.revision),
|
|
331
|
+
leafindex: Number(b.leafIndex)
|
|
332
|
+
});
|
|
333
|
+
return resp.path.map((p)=>Buffer.from(p));
|
|
334
|
+
}
|
|
335
|
+
case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
|
|
336
|
+
{
|
|
337
|
+
const b = body;
|
|
338
|
+
const resp = await this.api.wsdbGetBlockNumbersForLeafIndices({
|
|
339
|
+
treeid: b.treeId,
|
|
340
|
+
revision: toWsdbRevision(b.revision),
|
|
341
|
+
leafindices: b.leafIndices.map(Number)
|
|
342
|
+
});
|
|
343
|
+
return {
|
|
344
|
+
blockNumbers: resp.blocknumbers.map((n)=>n != null ? BigInt(n) : undefined)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
// ——— Find operations ———
|
|
348
|
+
case WorldStateMessageType.FIND_LEAF_INDICES:
|
|
349
|
+
{
|
|
350
|
+
const b = body;
|
|
351
|
+
const resp = await this.api.wsdbFindLeafIndices({
|
|
352
|
+
treeid: b.treeId,
|
|
353
|
+
revision: toWsdbRevision(b.revision),
|
|
354
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
355
|
+
startindex: Number(b.startIndex)
|
|
356
|
+
});
|
|
357
|
+
return {
|
|
358
|
+
indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
case WorldStateMessageType.FIND_LOW_LEAF:
|
|
362
|
+
{
|
|
363
|
+
const b = body;
|
|
364
|
+
const resp = await this.api.wsdbFindLowLeaf({
|
|
365
|
+
treeid: b.treeId,
|
|
366
|
+
revision: toWsdbRevision(b.revision),
|
|
367
|
+
key: new Uint8Array(b.key.toBuffer())
|
|
368
|
+
});
|
|
369
|
+
return {
|
|
370
|
+
alreadyPresent: resp.alreadypresent,
|
|
371
|
+
index: BigInt(resp.index)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
case WorldStateMessageType.FIND_SIBLING_PATHS:
|
|
375
|
+
{
|
|
376
|
+
const b = body;
|
|
377
|
+
const resp = await this.api.wsdbFindSiblingPaths({
|
|
378
|
+
treeid: b.treeId,
|
|
379
|
+
revision: toWsdbRevision(b.revision),
|
|
380
|
+
leaves: b.leaves.map(serializeLeafToBytes)
|
|
381
|
+
});
|
|
382
|
+
return {
|
|
383
|
+
paths: resp.paths.map(convertSiblingPathAndIndex)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
// ——— Mutations ———
|
|
387
|
+
case WorldStateMessageType.APPEND_LEAVES:
|
|
388
|
+
{
|
|
389
|
+
const b = body;
|
|
390
|
+
await this.api.wsdbAppendLeaves({
|
|
391
|
+
treeid: b.treeId,
|
|
392
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
393
|
+
forkid: b.forkId
|
|
394
|
+
});
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
case WorldStateMessageType.BATCH_INSERT:
|
|
398
|
+
{
|
|
399
|
+
const b = body;
|
|
400
|
+
const resp = await this.api.wsdbBatchInsert({
|
|
401
|
+
treeid: b.treeId,
|
|
402
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
403
|
+
subtreedepth: b.subtreeDepth,
|
|
404
|
+
forkid: b.forkId
|
|
405
|
+
});
|
|
406
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
407
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
408
|
+
}
|
|
409
|
+
case WorldStateMessageType.SEQUENTIAL_INSERT:
|
|
410
|
+
{
|
|
411
|
+
const b = body;
|
|
412
|
+
const resp = await this.api.wsdbSequentialInsert({
|
|
413
|
+
treeid: b.treeId,
|
|
414
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
415
|
+
forkid: b.forkId
|
|
416
|
+
});
|
|
417
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
418
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
419
|
+
}
|
|
420
|
+
case WorldStateMessageType.UPDATE_ARCHIVE:
|
|
421
|
+
{
|
|
422
|
+
const b = body;
|
|
423
|
+
await this.api.wsdbUpdateArchive({
|
|
424
|
+
blockstateref: blockStateRefToMap(b.blockStateRef),
|
|
425
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
426
|
+
forkid: b.forkId
|
|
427
|
+
});
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
430
|
+
// ——— Commit / Rollback ———
|
|
431
|
+
case WorldStateMessageType.COMMIT:
|
|
432
|
+
{
|
|
433
|
+
await this.api.wsdbCommit({});
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
case WorldStateMessageType.ROLLBACK:
|
|
437
|
+
{
|
|
438
|
+
await this.api.wsdbRollback({});
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
// ——— Block sync ———
|
|
442
|
+
case WorldStateMessageType.SYNC_BLOCK:
|
|
443
|
+
{
|
|
444
|
+
const b = body;
|
|
445
|
+
const resp = await this.api.wsdbSyncBlock({
|
|
446
|
+
blocknumber: Number(b.blockNumber),
|
|
447
|
+
blockstateref: blockStateRefToMap(b.blockStateRef),
|
|
448
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
449
|
+
// Forwarded so the wsdb (IPC) sync path enforces the same archive-root divergence check as the napi path.
|
|
450
|
+
expectedarchiveroot: new Uint8Array(b.expectedArchiveRoot),
|
|
451
|
+
expectedpreviousarchiveroot: new Uint8Array(b.expectedPreviousArchiveRoot),
|
|
452
|
+
paddednotehashes: b.paddedNoteHashes.map((l)=>new Uint8Array(l)),
|
|
453
|
+
paddedl1tol2messages: b.paddedL1ToL2Messages.map((l)=>new Uint8Array(l)),
|
|
454
|
+
paddednullifiers: b.paddedNullifiers.map((l)=>({
|
|
455
|
+
nullifier: new Uint8Array(l.nullifier)
|
|
456
|
+
})),
|
|
457
|
+
publicdatawrites: b.publicDataWrites.map((l)=>({
|
|
458
|
+
slot: new Uint8Array(l.slot),
|
|
459
|
+
value: new Uint8Array(l.value)
|
|
460
|
+
}))
|
|
461
|
+
});
|
|
462
|
+
return convertStatusFull(resp.status);
|
|
463
|
+
}
|
|
464
|
+
// ——— Fork management ———
|
|
465
|
+
case WorldStateMessageType.CREATE_FORK:
|
|
466
|
+
{
|
|
467
|
+
const b = body;
|
|
468
|
+
const resp = await this.api.wsdbCreateFork({
|
|
469
|
+
latest: b.latest,
|
|
470
|
+
blocknumber: Number(b.blockNumber)
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
forkId: resp.forkid
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
case WorldStateMessageType.DELETE_FORK:
|
|
477
|
+
{
|
|
478
|
+
const b = body;
|
|
479
|
+
await this.api.wsdbDeleteFork({
|
|
480
|
+
forkid: b.forkId
|
|
481
|
+
});
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
// ——— Block finalization ———
|
|
485
|
+
case WorldStateMessageType.FINALIZE_BLOCKS:
|
|
486
|
+
{
|
|
487
|
+
const b = body;
|
|
488
|
+
const resp = await this.api.wsdbFinalizeBlocks({
|
|
489
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
490
|
+
});
|
|
491
|
+
return convertStatusSummary(resp.status);
|
|
492
|
+
}
|
|
493
|
+
case WorldStateMessageType.UNWIND_BLOCKS:
|
|
494
|
+
{
|
|
495
|
+
const b = body;
|
|
496
|
+
const resp = await this.api.wsdbUnwindBlocks({
|
|
497
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
498
|
+
});
|
|
499
|
+
return convertStatusFull(resp.status);
|
|
500
|
+
}
|
|
501
|
+
case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
|
|
502
|
+
{
|
|
503
|
+
const b = body;
|
|
504
|
+
const resp = await this.api.wsdbRemoveHistoricalBlocks({
|
|
505
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
506
|
+
});
|
|
507
|
+
return convertStatusFull(resp.status);
|
|
508
|
+
}
|
|
509
|
+
// ——— Status ———
|
|
510
|
+
case WorldStateMessageType.GET_STATUS:
|
|
511
|
+
{
|
|
512
|
+
const resp = await this.api.wsdbGetStatus({});
|
|
513
|
+
return convertStatusSummary(resp.status);
|
|
514
|
+
}
|
|
515
|
+
// ——— Checkpoints ———
|
|
516
|
+
case WorldStateMessageType.CREATE_CHECKPOINT:
|
|
517
|
+
{
|
|
518
|
+
const b = body;
|
|
519
|
+
await this.api.wsdbCreateCheckpoint({
|
|
520
|
+
forkid: b.forkId
|
|
521
|
+
});
|
|
522
|
+
const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
|
|
523
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
524
|
+
return {
|
|
525
|
+
depth
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
case WorldStateMessageType.COMMIT_CHECKPOINT:
|
|
529
|
+
{
|
|
530
|
+
const b = body;
|
|
531
|
+
await this.api.wsdbCommitCheckpoint({
|
|
532
|
+
forkid: b.forkId
|
|
533
|
+
});
|
|
534
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
535
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
case WorldStateMessageType.REVERT_CHECKPOINT:
|
|
539
|
+
{
|
|
540
|
+
const b = body;
|
|
541
|
+
await this.api.wsdbRevertCheckpoint({
|
|
542
|
+
forkid: b.forkId
|
|
543
|
+
});
|
|
544
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
545
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
546
|
+
return undefined;
|
|
547
|
+
}
|
|
548
|
+
case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
|
|
549
|
+
{
|
|
550
|
+
const b = body;
|
|
551
|
+
const targetDepth = b.depth ?? 0;
|
|
552
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
553
|
+
if (targetDepth === 0) {
|
|
554
|
+
// Commit everything — use the bulk operation
|
|
555
|
+
await this.api.wsdbCommitAllCheckpoints({
|
|
556
|
+
forkid: b.forkId
|
|
557
|
+
});
|
|
558
|
+
} else {
|
|
559
|
+
// Commit one level at a time down to target depth
|
|
560
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
561
|
+
await this.api.wsdbCommitCheckpoint({
|
|
562
|
+
forkid: b.forkId
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
|
|
570
|
+
{
|
|
571
|
+
const b = body;
|
|
572
|
+
const targetDepth = b.depth ?? 0;
|
|
573
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
574
|
+
if (targetDepth === 0) {
|
|
575
|
+
// Revert everything — use the bulk operation
|
|
576
|
+
await this.api.wsdbRevertAllCheckpoints({
|
|
577
|
+
forkid: b.forkId
|
|
578
|
+
});
|
|
579
|
+
} else {
|
|
580
|
+
// Revert one level at a time down to target depth
|
|
581
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
582
|
+
await this.api.wsdbRevertCheckpoint({
|
|
583
|
+
forkid: b.forkId
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
// ——— Misc ———
|
|
591
|
+
case WorldStateMessageType.COPY_STORES:
|
|
592
|
+
{
|
|
593
|
+
const b = body;
|
|
594
|
+
await this.api.wsdbCopyStores({
|
|
595
|
+
dstpath: b.dstPath,
|
|
596
|
+
compact: b.compact
|
|
597
|
+
});
|
|
598
|
+
return undefined;
|
|
599
|
+
}
|
|
600
|
+
case WorldStateMessageType.CLOSE:
|
|
601
|
+
{
|
|
602
|
+
await this.api.wsdbShutdown({});
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
605
|
+
default:
|
|
606
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
634
609
|
}
|
|
635
610
|
/**
|
|
636
611
|
* Helper to create WsdbOptions from standard world state config.
|
|
637
|
-
* Returns the options needed to construct a
|
|
612
|
+
* Returns the options needed to construct a WsdbBackend.
|
|
638
613
|
*/ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
|
|
639
614
|
return {
|
|
640
615
|
treeHeights: {
|