@aztec/world-state 0.0.1-commit.96bb3f7 → 0.0.1-commit.993d240
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 +1 -1
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +9 -2
- package/dest/native/fork_checkpoint.d.ts +7 -1
- package/dest/native/fork_checkpoint.d.ts.map +1 -1
- package/dest/native/fork_checkpoint.js +15 -3
- package/dest/native/ipc_world_state_instance.d.ts +50 -0
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
- package/dest/native/ipc_world_state_instance.js +628 -0
- package/dest/native/merkle_trees_facade.d.ts +9 -6
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +45 -16
- package/dest/native/message.d.ts +13 -5
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/native_world_state.d.ts +34 -8
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +81 -27
- package/dest/native/native_world_state_instance.d.ts +6 -5
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +33 -26
- package/dest/native/world_state_ops_queue.js +5 -5
- package/dest/synchronizer/config.d.ts +3 -5
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +15 -18
- package/dest/synchronizer/factory.d.ts +6 -5
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +7 -6
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +4 -5
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +109 -37
- package/dest/test/utils.d.ts +7 -7
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +5 -5
- package/dest/testing.d.ts +4 -3
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +10 -6
- package/dest/world-state-db/merkle_tree_db.d.ts +3 -12
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/instrumentation/instrumentation.ts +9 -1
- package/src/native/fork_checkpoint.ts +19 -3
- package/src/native/ipc_world_state_instance.ts +717 -0
- package/src/native/merkle_trees_facade.ts +51 -17
- package/src/native/message.ts +14 -4
- package/src/native/native_world_state.ts +108 -32
- package/src/native/native_world_state_instance.ts +44 -32
- package/src/native/world_state_ops_queue.ts +5 -5
- package/src/synchronizer/config.ts +16 -23
- package/src/synchronizer/factory.ts +19 -11
- package/src/synchronizer/server_world_state_synchronizer.ts +119 -46
- package/src/test/utils.ts +6 -6
- package/src/testing.ts +8 -9
- package/src/world-state-db/merkle_tree_db.ts +2 -12
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { AsyncApi } from '@aztec/bb.js/aztec-wsdb';
|
|
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';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
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
|
+
}
|
|
19
|
+
// ————— Request conversion helpers —————
|
|
20
|
+
function toWsdbRevision(rev) {
|
|
21
|
+
return {
|
|
22
|
+
forkid: rev.forkId,
|
|
23
|
+
blocknumber: Number(rev.blockNumber),
|
|
24
|
+
includeuncommitted: rev.includeUncommitted
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function blockStateRefToMap(ref) {
|
|
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
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
// ————— Response conversion helpers —————
|
|
38
|
+
/** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
|
|
39
|
+
if (obj instanceof Uint8Array) {
|
|
40
|
+
return Buffer.from(obj);
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(obj)) {
|
|
43
|
+
return obj.map(convertUint8ArraysToBuffers);
|
|
44
|
+
}
|
|
45
|
+
if (obj !== null && typeof obj === 'object') {
|
|
46
|
+
const result = {};
|
|
47
|
+
for (const [key, value] of Object.entries(obj)){
|
|
48
|
+
result[key] = convertUint8ArraysToBuffers(value);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
return obj;
|
|
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
|
+
}
|
|
62
|
+
/** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
|
|
63
|
+
const result = {};
|
|
64
|
+
for (const [key, [root, size]] of Object.entries(state)){
|
|
65
|
+
result[Number(key)] = [
|
|
66
|
+
Buffer.from(root),
|
|
67
|
+
BigInt(size)
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/** Convert Wsdb WorldStateStatusSummary (lowercase) to NAPI format (camelCase). */ function convertStatusSummary(s) {
|
|
73
|
+
return {
|
|
74
|
+
unfinalizedBlockNumber: s.unfinalizedblocknumber,
|
|
75
|
+
finalizedBlockNumber: s.finalizedblocknumber,
|
|
76
|
+
oldestHistoricalBlock: s.oldesthistoricalblock,
|
|
77
|
+
treesAreSynched: s.treesaresynched
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function convertDBStats(s) {
|
|
81
|
+
return {
|
|
82
|
+
name: s.name,
|
|
83
|
+
numDataItems: s.numdataitems,
|
|
84
|
+
totalUsedSize: s.totalusedsize
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function convertTreeDBStats(s) {
|
|
88
|
+
return {
|
|
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)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function convertWorldStateDBStats(s) {
|
|
99
|
+
return {
|
|
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)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function convertTreeMeta(m) {
|
|
108
|
+
return {
|
|
109
|
+
name: m.name,
|
|
110
|
+
depth: m.depth,
|
|
111
|
+
size: m.size,
|
|
112
|
+
committedSize: m.committedsize,
|
|
113
|
+
root: m.root,
|
|
114
|
+
initialSize: m.initialsize,
|
|
115
|
+
initialRoot: m.initialroot,
|
|
116
|
+
oldestHistoricBlock: m.oldesthistoricblock,
|
|
117
|
+
unfinalizedBlockHeight: m.unfinalizedblockheight,
|
|
118
|
+
finalizedBlockHeight: m.finalizedblockheight
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function convertWorldStateMeta(m) {
|
|
122
|
+
return {
|
|
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)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function convertStatusFull(s) {
|
|
131
|
+
return {
|
|
132
|
+
summary: convertStatusSummary(s.summary),
|
|
133
|
+
dbStats: convertWorldStateDBStats(s.dbstats),
|
|
134
|
+
meta: convertWorldStateMeta(s.meta)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Convert Wsdb SiblingPathAndIndex to NAPI format. */ function convertSiblingPathAndIndex(s) {
|
|
138
|
+
if (!s) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
index: BigInt(s.index),
|
|
143
|
+
path: s.path.map((p)=>Buffer.from(p))
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* IPC-backed world state instance.
|
|
148
|
+
* Uses WsdbBackend (spawns aztec-wsdb binary) and the generated AsyncApi
|
|
149
|
+
* to communicate via the NamedUnion IPC protocol.
|
|
150
|
+
*/ export class IpcWorldState {
|
|
151
|
+
wsdbBackend;
|
|
152
|
+
instrumentation;
|
|
153
|
+
log;
|
|
154
|
+
open;
|
|
155
|
+
queues;
|
|
156
|
+
api;
|
|
157
|
+
/** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
|
|
158
|
+
constructor(wsdbBackend, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
|
|
159
|
+
this.wsdbBackend = wsdbBackend;
|
|
160
|
+
this.instrumentation = instrumentation;
|
|
161
|
+
this.log = log;
|
|
162
|
+
this.open = true;
|
|
163
|
+
this.queues = new Map();
|
|
164
|
+
this.checkpointDepths = new Map();
|
|
165
|
+
this.api = new AsyncApi(wsdbBackend);
|
|
166
|
+
this.queues.set(0, new WorldStateOpsQueue());
|
|
167
|
+
this.log.info('Created IPC-backed world state instance');
|
|
168
|
+
}
|
|
169
|
+
/** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
|
|
170
|
+
return this.wsdbBackend.getSocketPath();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
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
|
+
try {
|
|
202
|
+
const response = await requestQueue.execute(async ()=>{
|
|
203
|
+
assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
|
|
204
|
+
assert.equal(this.open, true, 'IPC instance is closed');
|
|
205
|
+
let response;
|
|
206
|
+
try {
|
|
207
|
+
response = await this._sendMessage(messageType, body);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
errorHandler(error.message);
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
return responseHandler(response);
|
|
213
|
+
}, messageType, committedOnly);
|
|
214
|
+
return response;
|
|
215
|
+
} finally{
|
|
216
|
+
if (messageType === WorldStateMessageType.DELETE_FORK) {
|
|
217
|
+
await requestQueue.stop();
|
|
218
|
+
this.queues.delete(forkId);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async close() {
|
|
223
|
+
if (!this.open) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.open = false;
|
|
227
|
+
const queue = this.queues.get(0);
|
|
228
|
+
// Send shutdown command. Under normal operation, the WSDB process sends its
|
|
229
|
+
// response before exiting (via ShutdownRequested in ipc_server.hpp). The
|
|
230
|
+
// try/catch is defensive: if the process is killed externally (SIGKILL, OOM)
|
|
231
|
+
// before responding, the pending IPC callback would be rejected by the socket
|
|
232
|
+
// close handler. We proceed to destroy the backend regardless.
|
|
233
|
+
try {
|
|
234
|
+
await queue.execute(async ()=>{
|
|
235
|
+
await this.api.wsdbShutdown({});
|
|
236
|
+
}, WorldStateMessageType.CLOSE, false);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
this.log.debug(`wsdbShutdown completed with error: ${err.message}`);
|
|
239
|
+
}
|
|
240
|
+
await queue.stop();
|
|
241
|
+
if (this.wsdbBackend.destroy) {
|
|
242
|
+
await this.wsdbBackend.destroy();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async _sendMessage(messageType, body) {
|
|
246
|
+
const start = performance.now();
|
|
247
|
+
try {
|
|
248
|
+
const response = await this.dispatch(messageType, body);
|
|
249
|
+
const durationMs = performance.now() - start;
|
|
250
|
+
this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
|
|
251
|
+
duration: durationMs
|
|
252
|
+
});
|
|
253
|
+
this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
|
|
254
|
+
return response;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async dispatch(messageType, body) {
|
|
261
|
+
switch(messageType){
|
|
262
|
+
// ——— Tree info & state reference ———
|
|
263
|
+
case WorldStateMessageType.GET_TREE_INFO:
|
|
264
|
+
{
|
|
265
|
+
const b = body;
|
|
266
|
+
const resp = await this.api.wsdbGetTreeInfo({
|
|
267
|
+
treeid: b.treeId,
|
|
268
|
+
revision: toWsdbRevision(b.revision)
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
treeId: resp.treeid,
|
|
272
|
+
root: Buffer.from(resp.root),
|
|
273
|
+
size: resp.size,
|
|
274
|
+
depth: resp.depth
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
case WorldStateMessageType.GET_STATE_REFERENCE:
|
|
278
|
+
{
|
|
279
|
+
const b = body;
|
|
280
|
+
const resp = await this.api.wsdbGetStateReference({
|
|
281
|
+
revision: toWsdbRevision(b.revision)
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
state: convertStateRef(resp.state)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
|
|
288
|
+
{
|
|
289
|
+
const resp = await this.api.wsdbGetInitialStateReference({});
|
|
290
|
+
return {
|
|
291
|
+
state: convertStateRef(resp.state)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// ——— Leaf queries ———
|
|
295
|
+
case WorldStateMessageType.GET_LEAF_VALUE:
|
|
296
|
+
{
|
|
297
|
+
const b = body;
|
|
298
|
+
const resp = await this.api.wsdbGetLeafValue({
|
|
299
|
+
treeid: b.treeId,
|
|
300
|
+
revision: toWsdbRevision(b.revision),
|
|
301
|
+
leafindex: Number(b.leafIndex)
|
|
302
|
+
});
|
|
303
|
+
if (!resp.value) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return decodeLeafValue(resp.value);
|
|
307
|
+
}
|
|
308
|
+
case WorldStateMessageType.GET_LEAF_PREIMAGE:
|
|
309
|
+
{
|
|
310
|
+
const b = body;
|
|
311
|
+
const resp = await this.api.wsdbGetLeafPreimage({
|
|
312
|
+
treeid: b.treeId,
|
|
313
|
+
revision: toWsdbRevision(b.revision),
|
|
314
|
+
leafindex: Number(b.leafIndex)
|
|
315
|
+
});
|
|
316
|
+
if (!resp.preimage) {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
return decodeLeafPreimage(resp.preimage);
|
|
320
|
+
}
|
|
321
|
+
case WorldStateMessageType.GET_SIBLING_PATH:
|
|
322
|
+
{
|
|
323
|
+
const b = body;
|
|
324
|
+
const resp = await this.api.wsdbGetSiblingPath({
|
|
325
|
+
treeid: b.treeId,
|
|
326
|
+
revision: toWsdbRevision(b.revision),
|
|
327
|
+
leafindex: Number(b.leafIndex)
|
|
328
|
+
});
|
|
329
|
+
return resp.path.map((p)=>Buffer.from(p));
|
|
330
|
+
}
|
|
331
|
+
case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
|
|
332
|
+
{
|
|
333
|
+
const b = body;
|
|
334
|
+
const resp = await this.api.wsdbGetBlockNumbersForLeafIndices({
|
|
335
|
+
treeid: b.treeId,
|
|
336
|
+
revision: toWsdbRevision(b.revision),
|
|
337
|
+
leafindices: b.leafIndices.map(Number)
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
blockNumbers: resp.blocknumbers.map((n)=>n != null ? BigInt(n) : undefined)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
// ——— Find operations ———
|
|
344
|
+
case WorldStateMessageType.FIND_LEAF_INDICES:
|
|
345
|
+
{
|
|
346
|
+
const b = body;
|
|
347
|
+
const resp = await this.api.wsdbFindLeafIndices({
|
|
348
|
+
treeid: b.treeId,
|
|
349
|
+
revision: toWsdbRevision(b.revision),
|
|
350
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
351
|
+
startindex: Number(b.startIndex)
|
|
352
|
+
});
|
|
353
|
+
return {
|
|
354
|
+
indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
case WorldStateMessageType.FIND_LOW_LEAF:
|
|
358
|
+
{
|
|
359
|
+
const b = body;
|
|
360
|
+
const resp = await this.api.wsdbFindLowLeaf({
|
|
361
|
+
treeid: b.treeId,
|
|
362
|
+
revision: toWsdbRevision(b.revision),
|
|
363
|
+
key: new Uint8Array(b.key.toBuffer())
|
|
364
|
+
});
|
|
365
|
+
return {
|
|
366
|
+
alreadyPresent: resp.alreadypresent,
|
|
367
|
+
index: BigInt(resp.index)
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
case WorldStateMessageType.FIND_SIBLING_PATHS:
|
|
371
|
+
{
|
|
372
|
+
const b = body;
|
|
373
|
+
const resp = await this.api.wsdbFindSiblingPaths({
|
|
374
|
+
treeid: b.treeId,
|
|
375
|
+
revision: toWsdbRevision(b.revision),
|
|
376
|
+
leaves: b.leaves.map(serializeLeafToBytes)
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
paths: resp.paths.map(convertSiblingPathAndIndex)
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
// ——— Mutations ———
|
|
383
|
+
case WorldStateMessageType.APPEND_LEAVES:
|
|
384
|
+
{
|
|
385
|
+
const b = body;
|
|
386
|
+
await this.api.wsdbAppendLeaves({
|
|
387
|
+
treeid: b.treeId,
|
|
388
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
389
|
+
forkid: b.forkId
|
|
390
|
+
});
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
case WorldStateMessageType.BATCH_INSERT:
|
|
394
|
+
{
|
|
395
|
+
const b = body;
|
|
396
|
+
const resp = await this.api.wsdbBatchInsert({
|
|
397
|
+
treeid: b.treeId,
|
|
398
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
399
|
+
subtreedepth: b.subtreeDepth,
|
|
400
|
+
forkid: b.forkId
|
|
401
|
+
});
|
|
402
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
403
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
404
|
+
}
|
|
405
|
+
case WorldStateMessageType.SEQUENTIAL_INSERT:
|
|
406
|
+
{
|
|
407
|
+
const b = body;
|
|
408
|
+
const resp = await this.api.wsdbSequentialInsert({
|
|
409
|
+
treeid: b.treeId,
|
|
410
|
+
leaves: b.leaves.map(serializeLeafToBytes),
|
|
411
|
+
forkid: b.forkId
|
|
412
|
+
});
|
|
413
|
+
const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
|
|
414
|
+
return convertUint8ArraysToBuffers(decoded);
|
|
415
|
+
}
|
|
416
|
+
case WorldStateMessageType.UPDATE_ARCHIVE:
|
|
417
|
+
{
|
|
418
|
+
const b = body;
|
|
419
|
+
await this.api.wsdbUpdateArchive({
|
|
420
|
+
blockstateref: blockStateRefToMap(b.blockStateRef),
|
|
421
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
422
|
+
forkid: b.forkId
|
|
423
|
+
});
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
// ——— Commit / Rollback ———
|
|
427
|
+
case WorldStateMessageType.COMMIT:
|
|
428
|
+
{
|
|
429
|
+
await this.api.wsdbCommit({});
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
case WorldStateMessageType.ROLLBACK:
|
|
433
|
+
{
|
|
434
|
+
await this.api.wsdbRollback({});
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
// ——— Block sync ———
|
|
438
|
+
case WorldStateMessageType.SYNC_BLOCK:
|
|
439
|
+
{
|
|
440
|
+
const b = body;
|
|
441
|
+
const resp = await this.api.wsdbSyncBlock({
|
|
442
|
+
blocknumber: Number(b.blockNumber),
|
|
443
|
+
blockstateref: blockStateRefToMap(b.blockStateRef),
|
|
444
|
+
blockheaderhash: new Uint8Array(b.blockHeaderHash),
|
|
445
|
+
paddednotehashes: b.paddedNoteHashes.map((l)=>new Uint8Array(l)),
|
|
446
|
+
paddedl1tol2messages: b.paddedL1ToL2Messages.map((l)=>new Uint8Array(l)),
|
|
447
|
+
paddednullifiers: b.paddedNullifiers.map((l)=>({
|
|
448
|
+
nullifier: new Uint8Array(l.nullifier)
|
|
449
|
+
})),
|
|
450
|
+
publicdatawrites: b.publicDataWrites.map((l)=>({
|
|
451
|
+
slot: new Uint8Array(l.slot),
|
|
452
|
+
value: new Uint8Array(l.value)
|
|
453
|
+
}))
|
|
454
|
+
});
|
|
455
|
+
return convertStatusFull(resp.status);
|
|
456
|
+
}
|
|
457
|
+
// ——— Fork management ———
|
|
458
|
+
case WorldStateMessageType.CREATE_FORK:
|
|
459
|
+
{
|
|
460
|
+
const b = body;
|
|
461
|
+
const resp = await this.api.wsdbCreateFork({
|
|
462
|
+
latest: b.latest,
|
|
463
|
+
blocknumber: Number(b.blockNumber)
|
|
464
|
+
});
|
|
465
|
+
return {
|
|
466
|
+
forkId: resp.forkid
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
case WorldStateMessageType.DELETE_FORK:
|
|
470
|
+
{
|
|
471
|
+
const b = body;
|
|
472
|
+
await this.api.wsdbDeleteFork({
|
|
473
|
+
forkid: b.forkId
|
|
474
|
+
});
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
// ——— Block finalization ———
|
|
478
|
+
case WorldStateMessageType.FINALIZE_BLOCKS:
|
|
479
|
+
{
|
|
480
|
+
const b = body;
|
|
481
|
+
const resp = await this.api.wsdbFinalizeBlocks({
|
|
482
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
483
|
+
});
|
|
484
|
+
return convertStatusSummary(resp.status);
|
|
485
|
+
}
|
|
486
|
+
case WorldStateMessageType.UNWIND_BLOCKS:
|
|
487
|
+
{
|
|
488
|
+
const b = body;
|
|
489
|
+
const resp = await this.api.wsdbUnwindBlocks({
|
|
490
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
491
|
+
});
|
|
492
|
+
return convertStatusFull(resp.status);
|
|
493
|
+
}
|
|
494
|
+
case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
|
|
495
|
+
{
|
|
496
|
+
const b = body;
|
|
497
|
+
const resp = await this.api.wsdbRemoveHistoricalBlocks({
|
|
498
|
+
toblocknumber: Number(b.toBlockNumber)
|
|
499
|
+
});
|
|
500
|
+
return convertStatusFull(resp.status);
|
|
501
|
+
}
|
|
502
|
+
// ——— Status ———
|
|
503
|
+
case WorldStateMessageType.GET_STATUS:
|
|
504
|
+
{
|
|
505
|
+
const resp = await this.api.wsdbGetStatus({});
|
|
506
|
+
return convertStatusSummary(resp.status);
|
|
507
|
+
}
|
|
508
|
+
// ——— Checkpoints ———
|
|
509
|
+
case WorldStateMessageType.CREATE_CHECKPOINT:
|
|
510
|
+
{
|
|
511
|
+
const b = body;
|
|
512
|
+
await this.api.wsdbCreateCheckpoint({
|
|
513
|
+
forkid: b.forkId
|
|
514
|
+
});
|
|
515
|
+
const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
|
|
516
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
517
|
+
return {
|
|
518
|
+
depth
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
case WorldStateMessageType.COMMIT_CHECKPOINT:
|
|
522
|
+
{
|
|
523
|
+
const b = body;
|
|
524
|
+
await this.api.wsdbCommitCheckpoint({
|
|
525
|
+
forkid: b.forkId
|
|
526
|
+
});
|
|
527
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
528
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
529
|
+
return undefined;
|
|
530
|
+
}
|
|
531
|
+
case WorldStateMessageType.REVERT_CHECKPOINT:
|
|
532
|
+
{
|
|
533
|
+
const b = body;
|
|
534
|
+
await this.api.wsdbRevertCheckpoint({
|
|
535
|
+
forkid: b.forkId
|
|
536
|
+
});
|
|
537
|
+
const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
|
|
538
|
+
this.checkpointDepths.set(b.forkId, depth);
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
|
|
542
|
+
{
|
|
543
|
+
const b = body;
|
|
544
|
+
const targetDepth = b.depth ?? 0;
|
|
545
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
546
|
+
if (targetDepth === 0) {
|
|
547
|
+
// Commit everything — use the bulk operation
|
|
548
|
+
await this.api.wsdbCommitAllCheckpoints({
|
|
549
|
+
forkid: b.forkId
|
|
550
|
+
});
|
|
551
|
+
} else {
|
|
552
|
+
// Commit one level at a time down to target depth
|
|
553
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
554
|
+
await this.api.wsdbCommitCheckpoint({
|
|
555
|
+
forkid: b.forkId
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
|
|
563
|
+
{
|
|
564
|
+
const b = body;
|
|
565
|
+
const targetDepth = b.depth ?? 0;
|
|
566
|
+
const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
|
|
567
|
+
if (targetDepth === 0) {
|
|
568
|
+
// Revert everything — use the bulk operation
|
|
569
|
+
await this.api.wsdbRevertAllCheckpoints({
|
|
570
|
+
forkid: b.forkId
|
|
571
|
+
});
|
|
572
|
+
} else {
|
|
573
|
+
// Revert one level at a time down to target depth
|
|
574
|
+
for(let d = currentDepth; d > targetDepth; d--){
|
|
575
|
+
await this.api.wsdbRevertCheckpoint({
|
|
576
|
+
forkid: b.forkId
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
this.checkpointDepths.set(b.forkId, targetDepth);
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
// ——— Misc ———
|
|
584
|
+
case WorldStateMessageType.COPY_STORES:
|
|
585
|
+
{
|
|
586
|
+
const b = body;
|
|
587
|
+
await this.api.wsdbCopyStores({
|
|
588
|
+
dstpath: b.dstPath,
|
|
589
|
+
compact: b.compact
|
|
590
|
+
});
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
593
|
+
case WorldStateMessageType.CLOSE:
|
|
594
|
+
{
|
|
595
|
+
await this.api.wsdbShutdown({});
|
|
596
|
+
return undefined;
|
|
597
|
+
}
|
|
598
|
+
default:
|
|
599
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Helper to create WsdbOptions from standard world state config.
|
|
605
|
+
* Returns the options needed to construct a WsdbBackend.
|
|
606
|
+
*/ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
|
|
607
|
+
return {
|
|
608
|
+
treeHeights: {
|
|
609
|
+
[MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
|
|
610
|
+
[MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
|
|
611
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
|
|
612
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
|
|
613
|
+
[MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
|
|
614
|
+
},
|
|
615
|
+
treePrefill: {
|
|
616
|
+
[MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
|
|
617
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
|
|
618
|
+
},
|
|
619
|
+
mapSizes: {
|
|
620
|
+
[MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
|
|
621
|
+
[MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
|
|
622
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
|
|
623
|
+
[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
|
|
624
|
+
[MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb
|
|
625
|
+
},
|
|
626
|
+
initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH
|
|
627
|
+
};
|
|
628
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { type IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
|
|
4
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
4
5
|
import type { BatchInsertionResult, IndexedTreeId, MerkleTreeLeafType, MerkleTreeReadOperations, MerkleTreeWriteOperations, SequentialInsertionResult, TreeInfo } from '@aztec/stdlib/interfaces/server';
|
|
5
6
|
import { MerkleTreeId, NullifierLeaf, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
|
|
6
7
|
import { type BlockHeader, StateReference } from '@aztec/stdlib/tx';
|
|
@@ -35,6 +36,7 @@ export declare class MerkleTreesFacade implements MerkleTreeReadOperations {
|
|
|
35
36
|
export declare class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTreeWriteOperations {
|
|
36
37
|
private opts;
|
|
37
38
|
private log;
|
|
39
|
+
private closePromise;
|
|
38
40
|
constructor(instance: NativeWorldStateInstance, initialHeader: BlockHeader, revision: WorldStateRevision, opts: {
|
|
39
41
|
closeDelayMs?: number;
|
|
40
42
|
});
|
|
@@ -43,12 +45,13 @@ export declare class MerkleTreesForkFacade extends MerkleTreesFacade implements
|
|
|
43
45
|
batchInsert<TreeHeight extends number, SubtreeSiblingPathHeight extends number, ID extends IndexedTreeId>(treeId: ID, rawLeaves: Buffer[], subtreeHeight: number): Promise<BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>>;
|
|
44
46
|
sequentialInsert<TreeHeight extends number, ID extends IndexedTreeId>(treeId: ID, rawLeaves: Buffer[]): Promise<SequentialInsertionResult<TreeHeight>>;
|
|
45
47
|
close(): Promise<void>;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
private doClose;
|
|
49
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
50
|
+
createCheckpoint(): Promise<number>;
|
|
48
51
|
commitCheckpoint(): Promise<void>;
|
|
49
52
|
revertCheckpoint(): Promise<void>;
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
commitAllCheckpointsTo(depth: number): Promise<void>;
|
|
54
|
+
revertAllCheckpointsTo(depth: number): Promise<void>;
|
|
52
55
|
}
|
|
53
|
-
export declare function serializeLeaf(leaf: Fr | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue;
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
56
|
+
export declare function serializeLeaf(leaf: Fr | BlockHash | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue;
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVya2xlX3RyZWVzX2ZhY2FkZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25hdGl2ZS9tZXJrbGVfdHJlZXNfZmFjYWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFJcEQsT0FBTyxFQUFFLEtBQUssdUJBQXVCLEVBQUUsV0FBVyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxFQUNWLG9CQUFvQixFQUNwQixhQUFhLEVBQ2Isa0JBQWtCLEVBQ2xCLHdCQUF3QixFQUN4Qix5QkFBeUIsRUFDekIseUJBQXlCLEVBQ3pCLFFBQVEsRUFDVCxNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFDTCxZQUFZLEVBQ1osYUFBYSxFQUViLGtCQUFrQixFQUVuQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxLQUFLLFdBQVcsRUFBeUIsY0FBYyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDM0YsT0FBTyxFQUFFLEtBQUssa0JBQWtCLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUlsRyxPQUFPLEVBRUwsS0FBSyxtQkFBbUIsRUFJekIsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUVqRixxQkFBYSxpQkFBa0IsWUFBVyx3QkFBd0I7SUFFOUQsU0FBUyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsd0JBQXdCO0lBQ3JELFNBQVMsQ0FBQyxRQUFRLENBQUMsYUFBYSxFQUFFLFdBQVc7SUFDN0MsU0FBUyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsa0JBQWtCO0lBSGpELFlBQ3FCLFFBQVEsRUFBRSx3QkFBd0IsRUFDbEMsYUFBYSxFQUFFLFdBQVcsRUFDMUIsUUFBUSxFQUFFLGtCQUFrQixFQUM3QztJQUVKLGdCQUFnQixJQUFJLFdBQVcsQ0FFOUI7SUFFRCxXQUFXLElBQUksNEJBQTRCLENBRTFDO0lBRUQsZUFBZSxDQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixDQUFDLFlBQVksQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FFakg7SUFFSyxnQkFBZ0IsQ0FBQyxDQUFDLFNBQVMsTUFBTSxFQUNyQyxNQUFNLEVBQUUsWUFBWSxFQUNwQixNQUFNLEVBQUUsa0JBQWtCLENBQUMsWUFBWSxDQUFDLEVBQUUsR0FDekMsT0FBTyxDQUFDLENBQUM7UUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQTtLQUFFLEdBQUcsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQWFsRTtJQUVLLG9CQUFvQixDQUN4QixNQUFNLEVBQUUsWUFBWSxFQUNwQixNQUFNLEVBQUUsa0JBQWtCLENBQUMsWUFBWSxDQUFDLEVBQUUsRUFDMUMsVUFBVSxFQUFFLE1BQU0sR0FDakIsT0FBTyxDQUFDLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FlakM7SUFFSyxlQUFlLENBQUMsTUFBTSxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyx1QkFBdUIsR0FBRyxTQUFTLENBQUMsQ0FRNUc7SUFFSyxZQUFZLENBQUMsRUFBRSxTQUFTLFlBQVksRUFDeEMsTUFBTSxFQUFFLEVBQUUsRUFDVixTQUFTLEVBQUUsTUFBTSxHQUNoQixPQUFPLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBaUI3QztJQUVLLHFCQUFxQixDQUN6QixNQUFNLEVBQUUsYUFBYSxFQUNyQixLQUFLLEVBQUUsTUFBTSxHQUNaLE9BQU8sQ0FBQztRQUFFLEtBQUssRUFBRSxNQUFNLENBQUM7UUFBQyxjQUFjLEVBQUUsT0FBTyxDQUFBO0tBQUUsR0FBRyxTQUFTLENBQUMsQ0FVakU7SUFFSyxjQUFjLENBQUMsQ0FBQyxTQUFTLE1BQU0sRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQVF2RztJQUVLLGlCQUFpQixJQUFJLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FhakQ7SUFFSyx3QkFBd0IsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLENBV3hEO0lBRUssV0FBVyxDQUFDLE1BQU0sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQVl6RDtJQUVLLDZCQUE2QixDQUFDLEVBQUUsU0FBUyxZQUFZLEVBQ3pELE1BQU0sRUFBRSxFQUFFLEVBQ1YsV0FBVyxFQUFFLE1BQU0sRUFBRSxHQUNwQixPQUFPLENBQUMsQ0FBQyxXQUFXLEdBQUcsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQVF0QztDQUNGO0FBRUQscUJBQWEscUJBQXNCLFNBQVEsaUJBQWtCLFlBQVcseUJBQXlCO0lBUTdGLE9BQU8sQ0FBQyxJQUFJO0lBUGQsT0FBTyxDQUFDLEdBQUcsQ0FBd0Q7SUFDbkUsT0FBTyxDQUFDLFlBQVksQ0FBNEI7SUFFaEQsWUFDRSxRQUFRLEVBQUUsd0JBQXdCLEVBQ2xDLGFBQWEsRUFBRSxXQUFXLEVBQzFCLFFBQVEsRUFBRSxrQkFBa0IsRUFDcEIsSUFBSSxFQUFFO1FBQUUsWUFBWSxDQUFDLEVBQUUsTUFBTSxDQUFBO0tBQUUsRUFLeEM7SUFDSyxhQUFhLENBQUMsTUFBTSxFQUFFLFdBQVcsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTXREO0lBRUssWUFBWSxDQUFDLEVBQUUsU0FBUyxZQUFZLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsa0JBQWtCLENBQUMsRUFBRSxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTXZHO0lBRUssV0FBVyxDQUFDLFVBQVUsU0FBUyxNQUFNLEVBQUUsd0JBQXdCLFNBQVMsTUFBTSxFQUFFLEVBQUUsU0FBUyxhQUFhLEVBQzVHLE1BQU0sRUFBRSxFQUFFLEVBQ1YsU0FBUyxFQUFFLE1BQU0sRUFBRSxFQUNuQixhQUFhLEVBQUUsTUFBTSxHQUNwQixPQUFPLENBQUMsb0JBQW9CLENBQUMsVUFBVSxFQUFFLHdCQUF3QixDQUFDLENBQUMsQ0F5QnJFO0lBRUssZ0JBQWdCLENBQUMsVUFBVSxTQUFTLE1BQU0sRUFBRSxFQUFFLFNBQVMsYUFBYSxFQUN4RSxNQUFNLEVBQUUsRUFBRSxFQUNWLFNBQVMsRUFBRSxNQUFNLEVBQUUsR0FDbEIsT0FBTyxDQUFDLHlCQUF5QixDQUFDLFVBQVUsQ0FBQyxDQUFDLENBb0JoRDtJQUVNLEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBUTVCO1lBRWEsT0FBTztJQW1CZixDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBVTNDO0lBRVksZ0JBQWdCLElBQUksT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUkvQztJQUVZLGdCQUFnQixJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FHN0M7SUFFWSxnQkFBZ0IsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBRzdDO0lBRVksc0JBQXNCLENBQUMsS0FBSyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTWhFO0lBRVksc0JBQXNCLENBQUMsS0FBSyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTWhFO0NBQ0Y7QUFnQkQsd0JBQWdCLGFBQWEsQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFHLFNBQVMsR0FBRyxhQUFhLEdBQUcsa0JBQWtCLEdBQUcsbUJBQW1CLENBVTVHIn0=
|