@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,3 +1,14 @@
|
|
|
1
|
+
import { AsyncApi } from '@aztec/bb.js/aztec-wsdb';
|
|
2
|
+
import type {
|
|
3
|
+
WorldStateDBStats as WsdbDBStats,
|
|
4
|
+
DBStats as WsdbDBStatsInner,
|
|
5
|
+
WorldStateMeta as WsdbMeta,
|
|
6
|
+
SiblingPathAndIndex as WsdbSiblingPathAndIndex,
|
|
7
|
+
WorldStateStatusFull as WsdbStatusFull,
|
|
8
|
+
WorldStateStatusSummary as WsdbStatusSummary,
|
|
9
|
+
TreeDBStats as WsdbTreeDBStats,
|
|
10
|
+
TreeMeta as WsdbTreeMeta,
|
|
11
|
+
} from '@aztec/bb.js/aztec-wsdb';
|
|
1
12
|
import {
|
|
2
13
|
ARCHIVE_HEIGHT,
|
|
3
14
|
DomainSeparator,
|
|
@@ -8,211 +19,62 @@ import {
|
|
|
8
19
|
NULLIFIER_TREE_HEIGHT,
|
|
9
20
|
PUBLIC_DATA_TREE_HEIGHT,
|
|
10
21
|
} from '@aztec/constants';
|
|
11
|
-
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
12
|
-
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
13
22
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
14
|
-
import type { IndexedTreeId, TreeInfo } from '@aztec/stdlib/interfaces/server';
|
|
15
23
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
16
|
-
import type {
|
|
17
|
-
import { AsyncApi, WsdbService } from '@aztec/wsdb';
|
|
18
|
-
import type {
|
|
19
|
-
WorldStateDBStats as WsdbDBStats,
|
|
20
|
-
DBStats as WsdbDBStatsInner,
|
|
21
|
-
WorldStateMeta as WsdbMeta,
|
|
22
|
-
SiblingPathAndIndex as WsdbSiblingPathAndIndex,
|
|
23
|
-
WorldStateStatusFull as WsdbStatusFull,
|
|
24
|
-
WorldStateStatusSummary as WsdbStatusSummary,
|
|
25
|
-
TreeDBStats as WsdbTreeDBStats,
|
|
26
|
-
TreeMeta as WsdbTreeMeta,
|
|
27
|
-
} from '@aztec/wsdb';
|
|
24
|
+
import type { WorldStateRevision } from '@aztec/stdlib/world-state';
|
|
28
25
|
|
|
29
|
-
import
|
|
26
|
+
import assert from 'assert';
|
|
27
|
+
import { Decoder, Encoder } from 'msgpackr';
|
|
30
28
|
|
|
31
29
|
import type { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
|
|
32
30
|
import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js';
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
import {
|
|
32
|
+
type DBStats,
|
|
33
|
+
type SerializedIndexedLeaf,
|
|
34
|
+
type SerializedLeafValue,
|
|
35
|
+
type TreeDBStats,
|
|
36
|
+
type TreeMeta,
|
|
37
|
+
type WorldStateDBStats,
|
|
38
|
+
WorldStateMessageType,
|
|
39
|
+
type WorldStateMeta,
|
|
40
|
+
type WorldStateRequest,
|
|
41
|
+
type WorldStateRequestCategories,
|
|
42
|
+
type WorldStateResponse,
|
|
43
|
+
type WorldStateStatusFull,
|
|
44
|
+
type WorldStateStatusSummary,
|
|
45
|
+
isWithCanonical,
|
|
46
|
+
isWithForkId,
|
|
47
|
+
isWithRevision,
|
|
48
48
|
} from './message.js';
|
|
49
49
|
import type { NativeWorldStateInstance } from './native_world_state_instance.js';
|
|
50
|
-
import
|
|
50
|
+
import { WorldStateOpsQueue } from './world_state_ops_queue.js';
|
|
51
51
|
|
|
52
|
-
// —————
|
|
52
|
+
// ————— Msgpack helpers —————
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
forkId: rev.forkId,
|
|
57
|
-
blockNumber: Number(rev.blockNumber),
|
|
58
|
-
includeUncommitted: rev.includeUncommitted,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
54
|
+
const msgpackEncoder = new Encoder({ useRecords: false });
|
|
55
|
+
const msgpackDecoder = new Decoder({ useRecords: false });
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
root: new Uint8Array(root),
|
|
66
|
-
size: Number(size),
|
|
67
|
-
}));
|
|
57
|
+
/** Msgpack-encode a SerializedLeafValue into bytes for IPC transport. */
|
|
58
|
+
function serializeLeafToBytes(leaf: SerializedLeafValue): Uint8Array {
|
|
59
|
+
return Buffer.from(msgpackEncoder.pack(leaf));
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
if (leaf instanceof Buffer || !('slot' in leaf)) {
|
|
72
|
-
throw new Error('Expected public data leaf');
|
|
73
|
-
}
|
|
74
|
-
return { slot: new Uint8Array(leaf.slot), value: new Uint8Array(leaf.value) };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function toNullifierLeaf(leaf: SerializedLeafValue): { nullifier: Uint8Array } {
|
|
78
|
-
if (leaf instanceof Buffer || !('nullifier' in leaf)) {
|
|
79
|
-
throw new Error('Expected nullifier leaf');
|
|
80
|
-
}
|
|
81
|
-
return { nullifier: new Uint8Array(leaf.nullifier) };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function fromPublicDataLeaf(leaf: { slot: Uint8Array; value: Uint8Array }): Exclude<SerializedLeafValue, Buffer> {
|
|
85
|
-
return { slot: Buffer.from(leaf.slot), value: Buffer.from(leaf.value) };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function fromNullifierLeaf(leaf: { nullifier: Uint8Array }): Exclude<SerializedLeafValue, Buffer> {
|
|
89
|
-
return { nullifier: Buffer.from(leaf.nullifier) };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
type WireIndexedLeaf<TLeaf> = { leaf: TLeaf; nextIndex: number; nextKey: Uint8Array };
|
|
93
|
-
type WireLeafUpdateWitnessData<TLeaf> = { leaf: WireIndexedLeaf<TLeaf>; index: number; path: Uint8Array[] };
|
|
94
|
-
|
|
95
|
-
function fromIndexedLeaf<TLeaf>(
|
|
96
|
-
leaf: WireIndexedLeaf<TLeaf>,
|
|
97
|
-
convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>,
|
|
98
|
-
) {
|
|
99
|
-
return {
|
|
100
|
-
leaf: convertLeaf(leaf.leaf),
|
|
101
|
-
nextIndex: leaf.nextIndex,
|
|
102
|
-
nextKey: Buffer.from(leaf.nextKey),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function fromLeafUpdateWitnessData<TLeaf>(
|
|
107
|
-
data: WireLeafUpdateWitnessData<TLeaf>,
|
|
108
|
-
convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>,
|
|
109
|
-
) {
|
|
110
|
-
return {
|
|
111
|
-
leaf: fromIndexedLeaf(data.leaf, convertLeaf),
|
|
112
|
-
index: data.index,
|
|
113
|
-
path: data.path.map(p => Buffer.from(p)),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function fromBatchInsertionResult<
|
|
118
|
-
TLeaf,
|
|
119
|
-
TResult extends {
|
|
120
|
-
lowLeafWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
121
|
-
sortedLeaves: Array<{ leaf: TLeaf; index: number }>;
|
|
122
|
-
subtreePath: Uint8Array[];
|
|
123
|
-
},
|
|
124
|
-
>(result: TResult, convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>) {
|
|
125
|
-
return {
|
|
126
|
-
lowLeafWitnessData: result.lowLeafWitnessData.map(data => fromLeafUpdateWitnessData(data, convertLeaf)),
|
|
127
|
-
sortedLeaves: result.sortedLeaves.map(({ leaf, index }) => [convertLeaf(leaf), index] as const),
|
|
128
|
-
subtreePath: result.subtreePath.map(p => Buffer.from(p)),
|
|
129
|
-
};
|
|
130
|
-
}
|
|
62
|
+
// ————— Request conversion helpers —————
|
|
131
63
|
|
|
132
|
-
function
|
|
133
|
-
TLeaf,
|
|
134
|
-
TResult extends {
|
|
135
|
-
lowLeafWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
136
|
-
insertionWitnessData: Array<WireLeafUpdateWitnessData<TLeaf>>;
|
|
137
|
-
},
|
|
138
|
-
>(result: TResult, convertLeaf: (leaf: TLeaf) => Exclude<SerializedLeafValue, Buffer>) {
|
|
64
|
+
function toWsdbRevision(rev: WorldStateRevision): { forkid: number; blocknumber: number; includeuncommitted: boolean } {
|
|
139
65
|
return {
|
|
140
|
-
|
|
141
|
-
|
|
66
|
+
forkid: rev.forkId,
|
|
67
|
+
blocknumber: Number(rev.blockNumber),
|
|
68
|
+
includeuncommitted: rev.includeUncommitted,
|
|
142
69
|
};
|
|
143
70
|
}
|
|
144
71
|
|
|
145
|
-
function
|
|
146
|
-
|
|
147
|
-
|
|
72
|
+
function blockStateRefToMap(ref: Map<number, readonly [Buffer, number | bigint]>): Map<number, [Uint8Array, number]> {
|
|
73
|
+
const result = new Map<number, [Uint8Array, number]>();
|
|
74
|
+
for (const [treeId, [root, size]] of ref.entries()) {
|
|
75
|
+
result.set(treeId, [new Uint8Array(root), Number(size)]);
|
|
148
76
|
}
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function formatMap(map: Record<number, number> | undefined): string | undefined {
|
|
153
|
-
if (!map || Object.keys(map).length === 0) {
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
return `{${Object.entries(map)
|
|
157
|
-
.map(([key, value]) => `${key}:${value}`)
|
|
158
|
-
.join(',')}}`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** SHM request/response ring size for the spawned aztec-wsdb process (32 MiB). */
|
|
162
|
-
const WSDB_SHM_RING_SIZE = 32 * 1024 * 1024;
|
|
163
|
-
|
|
164
|
-
function getWsdbThreadCount(): number {
|
|
165
|
-
return Math.min(16, cpus().length);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function getWsdbExtraArgs(
|
|
169
|
-
dataDir: string,
|
|
170
|
-
wsTreeMapSizes: WorldStateTreeMapSizes,
|
|
171
|
-
genesis: GenesisData,
|
|
172
|
-
threads: number,
|
|
173
|
-
): string[] {
|
|
174
|
-
const options = getWsdbOptions(dataDir, wsTreeMapSizes);
|
|
175
|
-
const args = ['--data-dir', dataDir, '--threads', threads.toString()];
|
|
176
|
-
|
|
177
|
-
// Size the SHM rings generously. Responses such as batch-insertion witnesses
|
|
178
|
-
// run to a few MB, and an SHM frame is capped at half the ring (the wrap
|
|
179
|
-
// handling needs that headroom), so 32 MiB rings give ~16 MiB per message.
|
|
180
|
-
// Ignored by the UDS transport. Pages are demand-faulted, so unused capacity
|
|
181
|
-
// is cheap.
|
|
182
|
-
args.push('--request-ring-size', WSDB_SHM_RING_SIZE.toString());
|
|
183
|
-
args.push('--response-ring-size', WSDB_SHM_RING_SIZE.toString());
|
|
184
|
-
|
|
185
|
-
const treeHeights = formatMap(options.treeHeights);
|
|
186
|
-
if (treeHeights) {
|
|
187
|
-
args.push('--tree-heights', treeHeights);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const treePrefill = formatMap(options.treePrefill);
|
|
191
|
-
if (treePrefill) {
|
|
192
|
-
args.push('--tree-prefill', treePrefill);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const mapSizes = formatMap(options.mapSizes);
|
|
196
|
-
if (mapSizes) {
|
|
197
|
-
args.push('--map-sizes', mapSizes);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
args.push('--initial-header-generator-point', options.initialHeaderGeneratorPoint.toString());
|
|
201
|
-
|
|
202
|
-
if (genesis.prefilledPublicData.length > 0) {
|
|
203
|
-
const pairs = genesis.prefilledPublicData.map(data => [
|
|
204
|
-
data.slot.toBuffer().toString('hex'),
|
|
205
|
-
data.value.toBuffer().toString('hex'),
|
|
206
|
-
]);
|
|
207
|
-
args.push('--prefilled-public-data', JSON.stringify(pairs));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const genesisTimestamp = Number(genesis.genesisTimestamp);
|
|
211
|
-
if (genesisTimestamp !== 0) {
|
|
212
|
-
args.push('--genesis-timestamp', genesisTimestamp.toString());
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return args;
|
|
77
|
+
return result;
|
|
216
78
|
}
|
|
217
79
|
|
|
218
80
|
// ————— Response conversion helpers —————
|
|
@@ -235,54 +97,66 @@ function convertUint8ArraysToBuffers(obj: unknown): unknown {
|
|
|
235
97
|
return obj;
|
|
236
98
|
}
|
|
237
99
|
|
|
100
|
+
/** Decode a msgpack-encoded leaf value blob and convert Uint8Arrays to Buffers. */
|
|
101
|
+
function decodeLeafValue(encoded: Uint8Array): SerializedLeafValue {
|
|
102
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
|
|
103
|
+
return convertUint8ArraysToBuffers(decoded) as SerializedLeafValue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Decode a msgpack-encoded indexed leaf preimage blob. */
|
|
107
|
+
function decodeLeafPreimage(encoded: Uint8Array): SerializedIndexedLeaf {
|
|
108
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
|
|
109
|
+
return convertUint8ArraysToBuffers(decoded) as SerializedIndexedLeaf;
|
|
110
|
+
}
|
|
111
|
+
|
|
238
112
|
/** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */
|
|
239
113
|
function convertStateRef(
|
|
240
|
-
state:
|
|
114
|
+
state: Record<number, [Uint8Array, number]>,
|
|
241
115
|
): Record<number, readonly [Buffer, number | bigint]> {
|
|
242
116
|
const result: Record<number, readonly [Buffer, number | bigint]> = {};
|
|
243
|
-
for (const
|
|
244
|
-
result[
|
|
117
|
+
for (const [key, [root, size]] of Object.entries(state)) {
|
|
118
|
+
result[Number(key)] = [Buffer.from(root), BigInt(size)] as const;
|
|
245
119
|
}
|
|
246
120
|
return result;
|
|
247
121
|
}
|
|
248
122
|
|
|
249
|
-
/** Convert Wsdb WorldStateStatusSummary to
|
|
123
|
+
/** Convert Wsdb WorldStateStatusSummary (lowercase) to NAPI format (camelCase). */
|
|
250
124
|
function convertStatusSummary(s: WsdbStatusSummary): WorldStateStatusSummary {
|
|
251
125
|
return {
|
|
252
|
-
unfinalizedBlockNumber: s.
|
|
253
|
-
finalizedBlockNumber: s.
|
|
254
|
-
oldestHistoricalBlock: s.
|
|
255
|
-
treesAreSynched: s.
|
|
126
|
+
unfinalizedBlockNumber: s.unfinalizedblocknumber,
|
|
127
|
+
finalizedBlockNumber: s.finalizedblocknumber,
|
|
128
|
+
oldestHistoricalBlock: s.oldesthistoricalblock,
|
|
129
|
+
treesAreSynched: s.treesaresynched,
|
|
256
130
|
} as unknown as WorldStateStatusSummary;
|
|
257
131
|
}
|
|
258
132
|
|
|
259
133
|
function convertDBStats(s: WsdbDBStatsInner): DBStats {
|
|
260
134
|
return {
|
|
261
135
|
name: s.name,
|
|
262
|
-
numDataItems: s.
|
|
263
|
-
totalUsedSize: s.
|
|
136
|
+
numDataItems: s.numdataitems,
|
|
137
|
+
totalUsedSize: s.totalusedsize,
|
|
264
138
|
} as unknown as DBStats;
|
|
265
139
|
}
|
|
266
140
|
|
|
267
141
|
function convertTreeDBStats(s: WsdbTreeDBStats): TreeDBStats {
|
|
268
142
|
return {
|
|
269
|
-
mapSize: s.
|
|
270
|
-
physicalFileSize: s.
|
|
271
|
-
blocksDBStats: convertDBStats(s.
|
|
272
|
-
nodesDBStats: convertDBStats(s.
|
|
273
|
-
leafPreimagesDBStats: convertDBStats(s.
|
|
274
|
-
leafIndicesDBStats: convertDBStats(s.
|
|
275
|
-
blockIndicesDBStats: convertDBStats(s.
|
|
143
|
+
mapSize: s.mapsize,
|
|
144
|
+
physicalFileSize: s.physicalfilesize,
|
|
145
|
+
blocksDBStats: convertDBStats(s.blocksdbstats),
|
|
146
|
+
nodesDBStats: convertDBStats(s.nodesdbstats),
|
|
147
|
+
leafPreimagesDBStats: convertDBStats(s.leafpreimagesdbstats),
|
|
148
|
+
leafIndicesDBStats: convertDBStats(s.leafindicesdbstats),
|
|
149
|
+
blockIndicesDBStats: convertDBStats(s.blockindicesdbstats),
|
|
276
150
|
} as unknown as TreeDBStats;
|
|
277
151
|
}
|
|
278
152
|
|
|
279
153
|
function convertWorldStateDBStats(s: WsdbDBStats): WorldStateDBStats {
|
|
280
154
|
return {
|
|
281
|
-
noteHashTreeStats: convertTreeDBStats(s.
|
|
282
|
-
messageTreeStats: convertTreeDBStats(s.
|
|
283
|
-
archiveTreeStats: convertTreeDBStats(s.
|
|
284
|
-
publicDataTreeStats: convertTreeDBStats(s.
|
|
285
|
-
nullifierTreeStats: convertTreeDBStats(s.
|
|
155
|
+
noteHashTreeStats: convertTreeDBStats(s.notehashtreestats),
|
|
156
|
+
messageTreeStats: convertTreeDBStats(s.messagetreestats),
|
|
157
|
+
archiveTreeStats: convertTreeDBStats(s.archivetreestats),
|
|
158
|
+
publicDataTreeStats: convertTreeDBStats(s.publicdatatreestats),
|
|
159
|
+
nullifierTreeStats: convertTreeDBStats(s.nullifiertreestats),
|
|
286
160
|
} as unknown as WorldStateDBStats;
|
|
287
161
|
}
|
|
288
162
|
|
|
@@ -291,37 +165,37 @@ function convertTreeMeta(m: WsdbTreeMeta): TreeMeta {
|
|
|
291
165
|
name: m.name,
|
|
292
166
|
depth: m.depth,
|
|
293
167
|
size: m.size,
|
|
294
|
-
committedSize: m.
|
|
168
|
+
committedSize: m.committedsize,
|
|
295
169
|
root: m.root,
|
|
296
|
-
initialSize: m.
|
|
297
|
-
initialRoot: m.
|
|
298
|
-
oldestHistoricBlock: m.
|
|
299
|
-
unfinalizedBlockHeight: m.
|
|
300
|
-
finalizedBlockHeight: m.
|
|
170
|
+
initialSize: m.initialsize,
|
|
171
|
+
initialRoot: m.initialroot,
|
|
172
|
+
oldestHistoricBlock: m.oldesthistoricblock,
|
|
173
|
+
unfinalizedBlockHeight: m.unfinalizedblockheight,
|
|
174
|
+
finalizedBlockHeight: m.finalizedblockheight,
|
|
301
175
|
} as unknown as TreeMeta;
|
|
302
176
|
}
|
|
303
177
|
|
|
304
178
|
function convertWorldStateMeta(m: WsdbMeta): WorldStateMeta {
|
|
305
179
|
return {
|
|
306
|
-
noteHashTreeMeta: convertTreeMeta(m.
|
|
307
|
-
messageTreeMeta: convertTreeMeta(m.
|
|
308
|
-
archiveTreeMeta: convertTreeMeta(m.
|
|
309
|
-
publicDataTreeMeta: convertTreeMeta(m.
|
|
310
|
-
nullifierTreeMeta: convertTreeMeta(m.
|
|
180
|
+
noteHashTreeMeta: convertTreeMeta(m.notehashtreemeta),
|
|
181
|
+
messageTreeMeta: convertTreeMeta(m.messagetreemeta),
|
|
182
|
+
archiveTreeMeta: convertTreeMeta(m.archivetreemeta),
|
|
183
|
+
publicDataTreeMeta: convertTreeMeta(m.publicdatatreemeta),
|
|
184
|
+
nullifierTreeMeta: convertTreeMeta(m.nullifiertreemeta),
|
|
311
185
|
} as unknown as WorldStateMeta;
|
|
312
186
|
}
|
|
313
187
|
|
|
314
188
|
function convertStatusFull(s: WsdbStatusFull): WorldStateStatusFull {
|
|
315
189
|
return {
|
|
316
190
|
summary: convertStatusSummary(s.summary),
|
|
317
|
-
dbStats: convertWorldStateDBStats(s.
|
|
191
|
+
dbStats: convertWorldStateDBStats(s.dbstats),
|
|
318
192
|
meta: convertWorldStateMeta(s.meta),
|
|
319
193
|
} as unknown as WorldStateStatusFull;
|
|
320
194
|
}
|
|
321
195
|
|
|
322
196
|
/** Convert Wsdb SiblingPathAndIndex to NAPI format. */
|
|
323
197
|
function convertSiblingPathAndIndex(
|
|
324
|
-
s: WsdbSiblingPathAndIndex |
|
|
198
|
+
s: WsdbSiblingPathAndIndex | undefined,
|
|
325
199
|
): { index: bigint; path: Buffer[] } | undefined {
|
|
326
200
|
if (!s) {
|
|
327
201
|
return undefined;
|
|
@@ -334,50 +208,108 @@ function convertSiblingPathAndIndex(
|
|
|
334
208
|
|
|
335
209
|
// ————— Public API —————
|
|
336
210
|
|
|
211
|
+
/** Backend interface matching WsdbBackend from bb.js. */
|
|
212
|
+
export interface WsdbIpcBackend {
|
|
213
|
+
call(inputBuffer: Uint8Array): Promise<Uint8Array>;
|
|
214
|
+
getSocketPath(): string;
|
|
215
|
+
destroy?(): Promise<void>;
|
|
216
|
+
}
|
|
217
|
+
|
|
337
218
|
/**
|
|
338
219
|
* IPC-backed world state instance.
|
|
339
|
-
* Uses
|
|
220
|
+
* Uses WsdbBackend (spawns aztec-wsdb binary) and the generated AsyncApi
|
|
340
221
|
* to communicate via the NamedUnion IPC protocol.
|
|
341
222
|
*/
|
|
342
223
|
export class IpcWorldState implements NativeWorldStateInstance {
|
|
343
224
|
private open = true;
|
|
225
|
+
private queues = new Map<number, WorldStateOpsQueue>();
|
|
344
226
|
private api: AsyncApi;
|
|
345
227
|
/** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */
|
|
346
228
|
private checkpointDepths = new Map<number, number>();
|
|
347
229
|
|
|
348
230
|
constructor(
|
|
349
|
-
private readonly
|
|
231
|
+
private readonly wsdbBackend: WsdbIpcBackend,
|
|
350
232
|
private readonly instrumentation: WorldStateInstrumentation,
|
|
351
233
|
bindings?: LoggerBindings,
|
|
352
234
|
private readonly log: Logger = createLogger('world-state:ipc-database', bindings),
|
|
353
235
|
) {
|
|
354
|
-
this.api =
|
|
236
|
+
this.api = new AsyncApi(wsdbBackend as any);
|
|
237
|
+
this.queues.set(0, new WorldStateOpsQueue());
|
|
355
238
|
this.log.info('Created IPC-backed world state instance');
|
|
356
239
|
}
|
|
357
240
|
|
|
241
|
+
/** Returns the socket path of the underlying wsdb server. */
|
|
242
|
+
getSocketPath(): string {
|
|
243
|
+
return this.wsdbBackend.getSocketPath();
|
|
244
|
+
}
|
|
245
|
+
|
|
358
246
|
/**
|
|
359
|
-
*
|
|
360
|
-
*
|
|
247
|
+
* Required by `NativeWorldStateInstance` for compatibility with the in-process
|
|
248
|
+
* NAPI path. The IPC backend does not expose an in-process pointer; callers that
|
|
249
|
+
* need to reach the WSDB process must use {@link getSocketPath} instead.
|
|
361
250
|
*/
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
251
|
+
getHandle(): any {
|
|
252
|
+
throw new Error('IpcWorldState has no in-process handle; use getSocketPath() instead');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async call<T extends WorldStateMessageType>(
|
|
256
|
+
messageType: T,
|
|
257
|
+
body: WorldStateRequest[T] & WorldStateRequestCategories,
|
|
258
|
+
responseHandler = (response: WorldStateResponse[T]): WorldStateResponse[T] => response,
|
|
259
|
+
errorHandler = (_: string) => {},
|
|
260
|
+
): Promise<WorldStateResponse[T]> {
|
|
261
|
+
let forkId = -1;
|
|
262
|
+
let committedOnly = false;
|
|
263
|
+
|
|
264
|
+
if (isWithCanonical(body)) {
|
|
265
|
+
forkId = 0;
|
|
266
|
+
} else if (isWithForkId(body)) {
|
|
267
|
+
forkId = body.forkId;
|
|
268
|
+
} else if (isWithRevision(body)) {
|
|
269
|
+
forkId = body.revision.forkId;
|
|
270
|
+
committedOnly = body.revision.includeUncommitted === false;
|
|
271
|
+
} else {
|
|
272
|
+
const _: never = body;
|
|
273
|
+
throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let requestQueue = this.queues.get(forkId);
|
|
277
|
+
if (requestQueue === undefined) {
|
|
278
|
+
requestQueue = new WorldStateOpsQueue();
|
|
279
|
+
this.queues.set(forkId, requestQueue);
|
|
280
|
+
}
|
|
378
281
|
|
|
379
|
-
|
|
380
|
-
|
|
282
|
+
// The per-fork queue is cleaned up in `finally` even on error, so the JS-side queues map cannot outlive
|
|
283
|
+
// the native fork (e.g. when the native fork was already destroyed by an unwind/historical-prune and
|
|
284
|
+
// DELETE_FORK rejects with "Fork not found").
|
|
285
|
+
let shouldDeleteForkQueue = false;
|
|
286
|
+
try {
|
|
287
|
+
const response = await requestQueue.execute(
|
|
288
|
+
async () => {
|
|
289
|
+
assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
|
|
290
|
+
assert.equal(this.open, true, 'IPC instance is closed');
|
|
291
|
+
let response: WorldStateResponse[T];
|
|
292
|
+
try {
|
|
293
|
+
response = await this._sendMessage(messageType, body);
|
|
294
|
+
} catch (error: any) {
|
|
295
|
+
errorHandler(error.message);
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
return responseHandler(response);
|
|
299
|
+
},
|
|
300
|
+
messageType,
|
|
301
|
+
committedOnly,
|
|
302
|
+
);
|
|
303
|
+
return response;
|
|
304
|
+
} catch (err: any) {
|
|
305
|
+
shouldDeleteForkQueue = forkId !== 0 && err?.message === 'Fork not found';
|
|
306
|
+
throw err;
|
|
307
|
+
} finally {
|
|
308
|
+
if (messageType === WorldStateMessageType.DELETE_FORK || shouldDeleteForkQueue) {
|
|
309
|
+
await requestQueue.stop();
|
|
310
|
+
this.queues.delete(forkId);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
381
313
|
}
|
|
382
314
|
|
|
383
315
|
async close(): Promise<void> {
|
|
@@ -385,417 +317,380 @@ export class IpcWorldState implements NativeWorldStateInstance {
|
|
|
385
317
|
return;
|
|
386
318
|
}
|
|
387
319
|
this.open = false;
|
|
388
|
-
|
|
389
|
-
}
|
|
320
|
+
const queue = this.queues.get(0)!;
|
|
390
321
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
322
|
+
// Send shutdown command. Under normal operation, the WSDB process sends its
|
|
323
|
+
// response before exiting (via ShutdownRequested in ipc_server.hpp). The
|
|
324
|
+
// try/catch is defensive: if the process is killed externally (SIGKILL, OOM)
|
|
325
|
+
// before responding, the pending IPC callback would be rejected by the socket
|
|
326
|
+
// close handler. We proceed to destroy the backend regardless.
|
|
327
|
+
try {
|
|
328
|
+
await queue.execute(
|
|
329
|
+
async () => {
|
|
330
|
+
await this.api.wsdbShutdown({});
|
|
331
|
+
},
|
|
332
|
+
WorldStateMessageType.CLOSE,
|
|
333
|
+
false,
|
|
334
|
+
);
|
|
335
|
+
} catch (err: any) {
|
|
336
|
+
this.log.debug(`wsdbShutdown completed with error: ${err.message}`);
|
|
337
|
+
}
|
|
338
|
+
await queue.stop();
|
|
402
339
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
return convertStateRef(resp.state);
|
|
407
|
-
});
|
|
340
|
+
if (this.wsdbBackend.destroy) {
|
|
341
|
+
await this.wsdbBackend.destroy();
|
|
342
|
+
}
|
|
408
343
|
}
|
|
409
344
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
345
|
+
private async _sendMessage<T extends WorldStateMessageType>(
|
|
346
|
+
messageType: T,
|
|
347
|
+
body: WorldStateRequest[T] & WorldStateRequestCategories,
|
|
348
|
+
): Promise<WorldStateResponse[T]> {
|
|
349
|
+
const start = performance.now();
|
|
350
|
+
try {
|
|
351
|
+
const response = await this.dispatch(messageType, body);
|
|
352
|
+
const durationMs = performance.now() - start;
|
|
353
|
+
this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, { duration: durationMs });
|
|
354
|
+
this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
|
|
355
|
+
return response;
|
|
356
|
+
} catch (error) {
|
|
357
|
+
this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
415
360
|
}
|
|
416
361
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
362
|
+
private async dispatch<T extends WorldStateMessageType>(
|
|
363
|
+
messageType: T,
|
|
364
|
+
body: WorldStateRequest[T] & WorldStateRequestCategories,
|
|
365
|
+
): Promise<WorldStateResponse[T]> {
|
|
366
|
+
switch (messageType) {
|
|
367
|
+
// ——— Tree info & state reference ———
|
|
368
|
+
|
|
369
|
+
case WorldStateMessageType.GET_TREE_INFO: {
|
|
370
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_TREE_INFO];
|
|
371
|
+
const resp = await this.api.wsdbGetTreeInfo({
|
|
372
|
+
treeid: b.treeId,
|
|
373
|
+
revision: toWsdbRevision(b.revision),
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
treeId: resp.treeid,
|
|
377
|
+
root: Buffer.from(resp.root),
|
|
378
|
+
size: resp.size,
|
|
379
|
+
depth: resp.depth,
|
|
380
|
+
} as WorldStateResponse[T];
|
|
429
381
|
}
|
|
430
382
|
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
383
|
+
case WorldStateMessageType.GET_STATE_REFERENCE: {
|
|
384
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_STATE_REFERENCE];
|
|
385
|
+
const resp = await this.api.wsdbGetStateReference({
|
|
386
|
+
revision: toWsdbRevision(b.revision),
|
|
387
|
+
});
|
|
388
|
+
return { state: convertStateRef(resp.state) } as WorldStateResponse[T];
|
|
434
389
|
}
|
|
435
390
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
391
|
+
case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE: {
|
|
392
|
+
const resp = await this.api.wsdbGetInitialStateReference({});
|
|
393
|
+
return { state: convertStateRef(resp.state) } as WorldStateResponse[T];
|
|
394
|
+
}
|
|
440
395
|
|
|
441
|
-
|
|
442
|
-
treeId: IndexedTreeId,
|
|
443
|
-
revision: WorldStateRevision,
|
|
444
|
-
leafIndex: bigint,
|
|
445
|
-
): Promise<SerializedIndexedLeaf | undefined> {
|
|
446
|
-
return this.execute('getLeafPreimage', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
447
|
-
const resp =
|
|
448
|
-
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
449
|
-
? await this.api.getPublicDataLeafPreimage({
|
|
450
|
-
revision: toWsdbRevision(revision),
|
|
451
|
-
leafIndex: Number(leafIndex),
|
|
452
|
-
})
|
|
453
|
-
: await this.api.getNullifierLeafPreimage({
|
|
454
|
-
revision: toWsdbRevision(revision),
|
|
455
|
-
leafIndex: Number(leafIndex),
|
|
456
|
-
});
|
|
457
|
-
return resp.preimage ? (convertUint8ArraysToBuffers(resp.preimage) as SerializedIndexedLeaf) : undefined;
|
|
458
|
-
});
|
|
459
|
-
}
|
|
396
|
+
// ——— Leaf queries ———
|
|
460
397
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
398
|
+
case WorldStateMessageType.GET_LEAF_VALUE: {
|
|
399
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_LEAF_VALUE];
|
|
400
|
+
const resp = await this.api.wsdbGetLeafValue({
|
|
401
|
+
treeid: b.treeId,
|
|
402
|
+
revision: toWsdbRevision(b.revision),
|
|
403
|
+
leafindex: Number(b.leafIndex),
|
|
404
|
+
});
|
|
405
|
+
if (!resp.value) {
|
|
406
|
+
return undefined as WorldStateResponse[T];
|
|
407
|
+
}
|
|
408
|
+
return decodeLeafValue(resp.value) as WorldStateResponse[T];
|
|
409
|
+
}
|
|
471
410
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
'getBlockNumbersForLeafIndices',
|
|
479
|
-
revision.forkId,
|
|
480
|
-
revision.includeUncommitted === false,
|
|
481
|
-
async () => {
|
|
482
|
-
const resp = await this.api.getBlockNumbersForLeafIndices({
|
|
483
|
-
treeId,
|
|
484
|
-
revision: toWsdbRevision(revision),
|
|
485
|
-
leafIndices: leafIndices.map(Number),
|
|
411
|
+
case WorldStateMessageType.GET_LEAF_PREIMAGE: {
|
|
412
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_LEAF_PREIMAGE];
|
|
413
|
+
const resp = await this.api.wsdbGetLeafPreimage({
|
|
414
|
+
treeid: b.treeId,
|
|
415
|
+
revision: toWsdbRevision(b.revision),
|
|
416
|
+
leafindex: Number(b.leafIndex),
|
|
486
417
|
});
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
418
|
+
if (!resp.preimage) {
|
|
419
|
+
return undefined as WorldStateResponse[T];
|
|
420
|
+
}
|
|
421
|
+
return decodeLeafPreimage(resp.preimage) as WorldStateResponse[T];
|
|
422
|
+
}
|
|
491
423
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const resp =
|
|
502
|
-
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
503
|
-
? await this.api.findPublicDataLeafIndices({
|
|
504
|
-
revision: wsdbRevision,
|
|
505
|
-
leaves: leaves.map(toPublicDataLeaf),
|
|
506
|
-
startIndex: wsdbStartIndex,
|
|
507
|
-
})
|
|
508
|
-
: treeId === MerkleTreeId.NULLIFIER_TREE
|
|
509
|
-
? await this.api.findNullifierLeafIndices({
|
|
510
|
-
revision: wsdbRevision,
|
|
511
|
-
leaves: leaves.map(toNullifierLeaf),
|
|
512
|
-
startIndex: wsdbStartIndex,
|
|
513
|
-
})
|
|
514
|
-
: await this.api.findLeafIndices({
|
|
515
|
-
treeId,
|
|
516
|
-
revision: wsdbRevision,
|
|
517
|
-
leaves: leaves.map(toFrLeaf),
|
|
518
|
-
startIndex: wsdbStartIndex,
|
|
519
|
-
});
|
|
520
|
-
return resp.indices.map(n => (n != null ? BigInt(n) : undefined));
|
|
521
|
-
});
|
|
522
|
-
}
|
|
424
|
+
case WorldStateMessageType.GET_SIBLING_PATH: {
|
|
425
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_SIBLING_PATH];
|
|
426
|
+
const resp = await this.api.wsdbGetSiblingPath({
|
|
427
|
+
treeid: b.treeId,
|
|
428
|
+
revision: toWsdbRevision(b.revision),
|
|
429
|
+
leafindex: Number(b.leafIndex),
|
|
430
|
+
});
|
|
431
|
+
return resp.path.map(p => Buffer.from(p)) as WorldStateResponse[T];
|
|
432
|
+
}
|
|
523
433
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
return {
|
|
536
|
-
alreadyPresent: resp.alreadyPresent,
|
|
537
|
-
index: BigInt(resp.index),
|
|
538
|
-
};
|
|
539
|
-
});
|
|
540
|
-
}
|
|
434
|
+
case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES: {
|
|
435
|
+
const b = body as WorldStateRequest[WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES];
|
|
436
|
+
const resp = await this.api.wsdbGetBlockNumbersForLeafIndices({
|
|
437
|
+
treeid: b.treeId,
|
|
438
|
+
revision: toWsdbRevision(b.revision),
|
|
439
|
+
leafindices: b.leafIndices.map(Number),
|
|
440
|
+
});
|
|
441
|
+
return {
|
|
442
|
+
blockNumbers: resp.blocknumbers.map(n => (n != null ? BigInt(n) : undefined)),
|
|
443
|
+
} as WorldStateResponse[T];
|
|
444
|
+
}
|
|
541
445
|
|
|
542
|
-
|
|
543
|
-
treeId: MerkleTreeId,
|
|
544
|
-
revision: WorldStateRevision,
|
|
545
|
-
leaves: SerializedLeafValue[],
|
|
546
|
-
): Promise<(SerializedSiblingPathAndIndex | undefined)[]> {
|
|
547
|
-
return this.execute('findSiblingPaths', revision.forkId, revision.includeUncommitted === false, async () => {
|
|
548
|
-
const wsdbRevision = toWsdbRevision(revision);
|
|
549
|
-
const resp =
|
|
550
|
-
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
551
|
-
? await this.api.findPublicDataSiblingPaths({
|
|
552
|
-
revision: wsdbRevision,
|
|
553
|
-
leaves: leaves.map(toPublicDataLeaf),
|
|
554
|
-
})
|
|
555
|
-
: treeId === MerkleTreeId.NULLIFIER_TREE
|
|
556
|
-
? await this.api.findNullifierSiblingPaths({
|
|
557
|
-
revision: wsdbRevision,
|
|
558
|
-
leaves: leaves.map(toNullifierLeaf),
|
|
559
|
-
})
|
|
560
|
-
: await this.api.findSiblingPaths({
|
|
561
|
-
treeId,
|
|
562
|
-
revision: wsdbRevision,
|
|
563
|
-
leaves: leaves.map(toFrLeaf),
|
|
564
|
-
});
|
|
565
|
-
return resp.paths.map(convertSiblingPathAndIndex);
|
|
566
|
-
});
|
|
567
|
-
}
|
|
446
|
+
// ——— Find operations ———
|
|
568
447
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
448
|
+
case WorldStateMessageType.FIND_LEAF_INDICES: {
|
|
449
|
+
const b = body as WorldStateRequest[WorldStateMessageType.FIND_LEAF_INDICES];
|
|
450
|
+
const resp = await this.api.wsdbFindLeafIndices({
|
|
451
|
+
treeid: b.treeId,
|
|
452
|
+
revision: toWsdbRevision(b.revision),
|
|
453
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
454
|
+
startindex: Number(b.startIndex),
|
|
455
|
+
});
|
|
456
|
+
return {
|
|
457
|
+
indices: resp.indices.map(n => (n != null ? BigInt(n) : undefined)),
|
|
458
|
+
} as WorldStateResponse[T];
|
|
459
|
+
}
|
|
578
460
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
461
|
+
case WorldStateMessageType.FIND_LOW_LEAF: {
|
|
462
|
+
const b = body as WorldStateRequest[WorldStateMessageType.FIND_LOW_LEAF];
|
|
463
|
+
const resp = await this.api.wsdbFindLowLeaf({
|
|
464
|
+
treeid: b.treeId,
|
|
465
|
+
revision: toWsdbRevision(b.revision),
|
|
466
|
+
key: new Uint8Array(b.key.toBuffer()),
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
alreadyPresent: resp.alreadypresent,
|
|
470
|
+
index: BigInt(resp.index),
|
|
471
|
+
} as WorldStateResponse[T];
|
|
587
472
|
}
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
473
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
subtreeDepth,
|
|
603
|
-
forkId,
|
|
604
|
-
})
|
|
605
|
-
: await this.api.batchInsertNullifier({
|
|
606
|
-
leaves: leaves.map(toNullifierLeaf),
|
|
607
|
-
subtreeDepth,
|
|
608
|
-
forkId,
|
|
609
|
-
});
|
|
610
|
-
return treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
611
|
-
? fromBatchInsertionResult(resp.result as any, fromPublicDataLeaf)
|
|
612
|
-
: fromBatchInsertionResult(resp.result as any, fromNullifierLeaf);
|
|
613
|
-
});
|
|
614
|
-
}
|
|
474
|
+
case WorldStateMessageType.FIND_SIBLING_PATHS: {
|
|
475
|
+
const b = body as WorldStateRequest[WorldStateMessageType.FIND_SIBLING_PATHS];
|
|
476
|
+
const resp = await this.api.wsdbFindSiblingPaths({
|
|
477
|
+
treeid: b.treeId,
|
|
478
|
+
revision: toWsdbRevision(b.revision),
|
|
479
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
paths: resp.paths.map(convertSiblingPathAndIndex),
|
|
483
|
+
} as WorldStateResponse[T];
|
|
484
|
+
}
|
|
615
485
|
|
|
616
|
-
|
|
617
|
-
treeId: IndexedTreeId,
|
|
618
|
-
forkId: number,
|
|
619
|
-
leaves: SerializedLeafValue[],
|
|
620
|
-
): Promise<SerializedSequentialInsertionResult> {
|
|
621
|
-
return this.execute('sequentialInsert', forkId, false, async () => {
|
|
622
|
-
const resp =
|
|
623
|
-
treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
624
|
-
? await this.api.sequentialInsertPublicData({
|
|
625
|
-
leaves: leaves.map(toPublicDataLeaf),
|
|
626
|
-
forkId,
|
|
627
|
-
})
|
|
628
|
-
: await this.api.sequentialInsertNullifier({
|
|
629
|
-
leaves: leaves.map(toNullifierLeaf),
|
|
630
|
-
forkId,
|
|
631
|
-
});
|
|
632
|
-
return treeId === MerkleTreeId.PUBLIC_DATA_TREE
|
|
633
|
-
? fromSequentialInsertionResult(resp.result as any, fromPublicDataLeaf)
|
|
634
|
-
: fromSequentialInsertionResult(resp.result as any, fromNullifierLeaf);
|
|
635
|
-
});
|
|
636
|
-
}
|
|
486
|
+
// ——— Mutations ———
|
|
637
487
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
return this.execute('syncBlock', 0, false, async () => {
|
|
648
|
-
const resp = await this.api.syncBlock({
|
|
649
|
-
blockNumber: Number(input.blockNumber),
|
|
650
|
-
blockStateRef: blockStateRefToMap(input.blockStateRef),
|
|
651
|
-
blockHeaderHash: new Uint8Array(input.blockHeaderHash),
|
|
652
|
-
paddedNoteHashes: input.paddedNoteHashes.map(toFrLeaf),
|
|
653
|
-
paddedL1ToL2Messages: input.paddedL1ToL2Messages.map(toFrLeaf),
|
|
654
|
-
paddedNullifiers: input.paddedNullifiers.map(toNullifierLeaf),
|
|
655
|
-
publicDataWrites: input.publicDataWrites.map(toPublicDataLeaf),
|
|
656
|
-
});
|
|
657
|
-
return convertStatusFull(resp.status);
|
|
658
|
-
});
|
|
659
|
-
}
|
|
488
|
+
case WorldStateMessageType.APPEND_LEAVES: {
|
|
489
|
+
const b = body as WorldStateRequest[WorldStateMessageType.APPEND_LEAVES];
|
|
490
|
+
await this.api.wsdbAppendLeaves({
|
|
491
|
+
treeid: b.treeId,
|
|
492
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
493
|
+
forkid: b.forkId,
|
|
494
|
+
});
|
|
495
|
+
return undefined as WorldStateResponse[T];
|
|
496
|
+
}
|
|
660
497
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
498
|
+
case WorldStateMessageType.BATCH_INSERT: {
|
|
499
|
+
const b = body as WorldStateRequest[WorldStateMessageType.BATCH_INSERT];
|
|
500
|
+
const resp = await this.api.wsdbBatchInsert({
|
|
501
|
+
treeid: b.treeId,
|
|
502
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
503
|
+
subtreedepth: b.subtreeDepth,
|
|
504
|
+
forkid: b.forkId,
|
|
505
|
+
});
|
|
506
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
507
|
+
return convertUint8ArraysToBuffers(decoded) as WorldStateResponse[T];
|
|
508
|
+
}
|
|
670
509
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
510
|
+
case WorldStateMessageType.SEQUENTIAL_INSERT: {
|
|
511
|
+
const b = body as WorldStateRequest[WorldStateMessageType.SEQUENTIAL_INSERT];
|
|
512
|
+
const resp = await this.api.wsdbSequentialInsert({
|
|
513
|
+
treeid: b.treeId,
|
|
514
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
515
|
+
forkid: b.forkId,
|
|
516
|
+
});
|
|
517
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
518
|
+
return convertUint8ArraysToBuffers(decoded) as WorldStateResponse[T];
|
|
519
|
+
}
|
|
676
520
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
521
|
+
case WorldStateMessageType.UPDATE_ARCHIVE: {
|
|
522
|
+
const b = body as WorldStateRequest[WorldStateMessageType.UPDATE_ARCHIVE];
|
|
523
|
+
await this.api.wsdbUpdateArchive({
|
|
524
|
+
blockstateref: blockStateRefToMap(b.blockStateRef as Map<number, readonly [Buffer, number | bigint]>) as any,
|
|
525
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
526
|
+
forkid: b.forkId,
|
|
527
|
+
});
|
|
528
|
+
return undefined as WorldStateResponse[T];
|
|
529
|
+
}
|
|
683
530
|
|
|
684
|
-
|
|
685
|
-
return this.execute('unwindBlocks', 0, false, async () => {
|
|
686
|
-
const resp = await this.api.unwindBlocks({ toBlockNumber: Number(toBlockNumber) });
|
|
687
|
-
return convertStatusFull(resp.status);
|
|
688
|
-
});
|
|
689
|
-
}
|
|
531
|
+
// ——— Commit / Rollback ———
|
|
690
532
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
});
|
|
696
|
-
}
|
|
533
|
+
case WorldStateMessageType.COMMIT: {
|
|
534
|
+
await this.api.wsdbCommit({});
|
|
535
|
+
return undefined as WorldStateResponse[T];
|
|
536
|
+
}
|
|
697
537
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
});
|
|
703
|
-
}
|
|
538
|
+
case WorldStateMessageType.ROLLBACK: {
|
|
539
|
+
await this.api.wsdbRollback({});
|
|
540
|
+
return undefined as WorldStateResponse[T];
|
|
541
|
+
}
|
|
704
542
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
543
|
+
// ——— Block sync ———
|
|
544
|
+
|
|
545
|
+
case WorldStateMessageType.SYNC_BLOCK: {
|
|
546
|
+
const b = body as WorldStateRequest[WorldStateMessageType.SYNC_BLOCK];
|
|
547
|
+
const resp = await this.api.wsdbSyncBlock({
|
|
548
|
+
blocknumber: Number(b.blockNumber),
|
|
549
|
+
blockstateref: blockStateRefToMap(b.blockStateRef as Map<number, readonly [Buffer, number | bigint]>) as any,
|
|
550
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
551
|
+
// Forwarded so the wsdb (IPC) sync path enforces the same archive-root divergence check as the napi path.
|
|
552
|
+
expectedarchiveroot: new Uint8Array(b.expectedArchiveRoot),
|
|
553
|
+
expectedpreviousarchiveroot: new Uint8Array(b.expectedPreviousArchiveRoot),
|
|
554
|
+
paddednotehashes: b.paddedNoteHashes.map(l => new Uint8Array(l as Buffer)),
|
|
555
|
+
paddedl1tol2messages: b.paddedL1ToL2Messages.map(l => new Uint8Array(l as Buffer)),
|
|
556
|
+
paddednullifiers: b.paddedNullifiers.map(l => ({
|
|
557
|
+
nullifier: new Uint8Array((l as { nullifier: Buffer }).nullifier),
|
|
558
|
+
})),
|
|
559
|
+
publicdatawrites: b.publicDataWrites.map(l => ({
|
|
560
|
+
slot: new Uint8Array((l as { slot: Buffer; value: Buffer }).slot),
|
|
561
|
+
value: new Uint8Array((l as { slot: Buffer; value: Buffer }).value),
|
|
562
|
+
})),
|
|
563
|
+
});
|
|
713
564
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
await this.api.commitCheckpoint({ forkId });
|
|
717
|
-
const depth = Math.max(0, (this.checkpointDepths.get(forkId) ?? 0) - 1);
|
|
718
|
-
this.checkpointDepths.set(forkId, depth);
|
|
719
|
-
});
|
|
720
|
-
}
|
|
565
|
+
return convertStatusFull(resp.status) as WorldStateResponse[T];
|
|
566
|
+
}
|
|
721
567
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
568
|
+
// ——— Fork management ———
|
|
569
|
+
|
|
570
|
+
case WorldStateMessageType.CREATE_FORK: {
|
|
571
|
+
const b = body as WorldStateRequest[WorldStateMessageType.CREATE_FORK];
|
|
572
|
+
const resp = await this.api.wsdbCreateFork({
|
|
573
|
+
latest: b.latest,
|
|
574
|
+
blocknumber: Number(b.blockNumber),
|
|
575
|
+
});
|
|
576
|
+
return { forkId: resp.forkid } as WorldStateResponse[T];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
case WorldStateMessageType.DELETE_FORK: {
|
|
580
|
+
const b = body as WorldStateRequest[WorldStateMessageType.DELETE_FORK];
|
|
581
|
+
await this.api.wsdbDeleteFork({ forkid: b.forkId });
|
|
582
|
+
return undefined as WorldStateResponse[T];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// ——— Block finalization ———
|
|
586
|
+
|
|
587
|
+
case WorldStateMessageType.FINALIZE_BLOCKS: {
|
|
588
|
+
const b = body as WorldStateRequest[WorldStateMessageType.FINALIZE_BLOCKS];
|
|
589
|
+
const resp = await this.api.wsdbFinalizeBlocks({ toblocknumber: Number(b.toBlockNumber) });
|
|
590
|
+
return convertStatusSummary(resp.status) as WorldStateResponse[T];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
case WorldStateMessageType.UNWIND_BLOCKS: {
|
|
594
|
+
const b = body as WorldStateRequest[WorldStateMessageType.UNWIND_BLOCKS];
|
|
595
|
+
const resp = await this.api.wsdbUnwindBlocks({ toblocknumber: Number(b.toBlockNumber) });
|
|
596
|
+
return convertStatusFull(resp.status) as WorldStateResponse[T];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS: {
|
|
600
|
+
const b = body as WorldStateRequest[WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS];
|
|
601
|
+
const resp = await this.api.wsdbRemoveHistoricalBlocks({ toblocknumber: Number(b.toBlockNumber) });
|
|
602
|
+
return convertStatusFull(resp.status) as WorldStateResponse[T];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ——— Status ———
|
|
606
|
+
|
|
607
|
+
case WorldStateMessageType.GET_STATUS: {
|
|
608
|
+
const resp = await this.api.wsdbGetStatus({});
|
|
609
|
+
return convertStatusSummary(resp.status) as WorldStateResponse[T];
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ——— Checkpoints ———
|
|
613
|
+
|
|
614
|
+
case WorldStateMessageType.CREATE_CHECKPOINT: {
|
|
615
|
+
const b = body as WorldStateRequest[WorldStateMessageType.CREATE_CHECKPOINT];
|
|
616
|
+
await this.api.wsdbCreateCheckpoint({ forkid: b.forkId });
|
|
617
|
+
const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
|
|
618
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
619
|
+
return { depth } as WorldStateResponse[T];
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
case WorldStateMessageType.COMMIT_CHECKPOINT: {
|
|
623
|
+
const b = body as WorldStateRequest[WorldStateMessageType.COMMIT_CHECKPOINT];
|
|
624
|
+
await this.api.wsdbCommitCheckpoint({ forkid: b.forkId });
|
|
625
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
626
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
627
|
+
return undefined as WorldStateResponse[T];
|
|
628
|
+
}
|
|
729
629
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
630
|
+
case WorldStateMessageType.REVERT_CHECKPOINT: {
|
|
631
|
+
const b = body as WorldStateRequest[WorldStateMessageType.REVERT_CHECKPOINT];
|
|
632
|
+
await this.api.wsdbRevertCheckpoint({ forkid: b.forkId });
|
|
633
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
634
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
635
|
+
return undefined as WorldStateResponse[T];
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS: {
|
|
639
|
+
const b = body as WorldStateRequest[WorldStateMessageType.COMMIT_ALL_CHECKPOINTS];
|
|
640
|
+
const targetDepth = b.depth ?? 0;
|
|
641
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
642
|
+
if (targetDepth === 0) {
|
|
643
|
+
// Commit everything — use the bulk operation
|
|
644
|
+
await this.api.wsdbCommitAllCheckpoints({ forkid: b.forkId });
|
|
645
|
+
} else {
|
|
646
|
+
// Commit one level at a time down to target depth
|
|
647
|
+
for (let d = currentDepth; d > targetDepth; d--) {
|
|
648
|
+
await this.api.wsdbCommitCheckpoint({ forkid: b.forkId });
|
|
649
|
+
}
|
|
738
650
|
}
|
|
651
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
652
|
+
return undefined as WorldStateResponse[T];
|
|
739
653
|
}
|
|
740
|
-
this.checkpointDepths.set(forkId, depth);
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
654
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
655
|
+
case WorldStateMessageType.REVERT_ALL_CHECKPOINTS: {
|
|
656
|
+
const b = body as WorldStateRequest[WorldStateMessageType.REVERT_ALL_CHECKPOINTS];
|
|
657
|
+
const targetDepth = b.depth ?? 0;
|
|
658
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
659
|
+
if (targetDepth === 0) {
|
|
660
|
+
// Revert everything — use the bulk operation
|
|
661
|
+
await this.api.wsdbRevertAllCheckpoints({ forkid: b.forkId });
|
|
662
|
+
} else {
|
|
663
|
+
// Revert one level at a time down to target depth
|
|
664
|
+
for (let d = currentDepth; d > targetDepth; d--) {
|
|
665
|
+
await this.api.wsdbRevertCheckpoint({ forkid: b.forkId });
|
|
666
|
+
}
|
|
752
667
|
}
|
|
668
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
669
|
+
return undefined as WorldStateResponse[T];
|
|
753
670
|
}
|
|
754
|
-
this.checkpointDepths.set(forkId, depth);
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
671
|
|
|
758
|
-
|
|
759
|
-
return this.execute('copyStores', 0, false, async () => {
|
|
760
|
-
await this.api.copyStores({ dstPath, compact });
|
|
761
|
-
});
|
|
762
|
-
}
|
|
672
|
+
// ——— Misc ———
|
|
763
673
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
private async execute<T>(
|
|
770
|
-
operation: WorldStateOperationName,
|
|
771
|
-
_forkId: number,
|
|
772
|
-
_committedOnly: boolean,
|
|
773
|
-
request: () => Promise<T>,
|
|
774
|
-
): Promise<T> {
|
|
775
|
-
if (!this.open) {
|
|
776
|
-
throw new Error('IPC instance is closed');
|
|
777
|
-
}
|
|
778
|
-
return await this.send(operation, request);
|
|
779
|
-
}
|
|
674
|
+
case WorldStateMessageType.COPY_STORES: {
|
|
675
|
+
const b = body as WorldStateRequest[WorldStateMessageType.COPY_STORES];
|
|
676
|
+
await this.api.wsdbCopyStores({ dstpath: b.dstPath, compact: b.compact });
|
|
677
|
+
return undefined as WorldStateResponse[T];
|
|
678
|
+
}
|
|
780
679
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
return response;
|
|
789
|
-
} catch (error) {
|
|
790
|
-
this.log.error(`Call ${operation} failed: ${error}`, error);
|
|
791
|
-
throw error;
|
|
680
|
+
case WorldStateMessageType.CLOSE: {
|
|
681
|
+
await this.api.wsdbShutdown({});
|
|
682
|
+
return undefined as WorldStateResponse[T];
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
default:
|
|
686
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
792
687
|
}
|
|
793
688
|
}
|
|
794
689
|
}
|
|
795
690
|
|
|
796
691
|
/**
|
|
797
692
|
* Helper to create WsdbOptions from standard world state config.
|
|
798
|
-
* Returns the options needed to construct a
|
|
693
|
+
* Returns the options needed to construct a WsdbBackend.
|
|
799
694
|
*/
|
|
800
695
|
export function getWsdbOptions(
|
|
801
696
|
dataDir: string,
|