@aztec/prover-client 0.65.2 → 0.67.0
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/block_builder/index.d.ts +6 -0
- package/dest/block_builder/index.d.ts.map +1 -0
- package/dest/block_builder/index.js +2 -0
- package/dest/block_builder/light.d.ts +32 -0
- package/dest/block_builder/light.d.ts.map +1 -0
- package/dest/block_builder/light.js +75 -0
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -3
- package/dest/mocks/fixtures.d.ts +4 -5
- package/dest/mocks/fixtures.d.ts.map +1 -1
- package/dest/mocks/fixtures.js +4 -8
- package/dest/mocks/test_context.d.ts +30 -12
- package/dest/mocks/test_context.d.ts.map +1 -1
- package/dest/mocks/test_context.js +61 -24
- package/dest/orchestrator/block-building-helpers.d.ts +5 -5
- package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
- package/dest/orchestrator/block-building-helpers.js +10 -11
- package/dest/orchestrator/epoch-proving-state.d.ts +5 -6
- package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/epoch-proving-state.js +10 -12
- package/dest/orchestrator/orchestrator.d.ts +8 -6
- package/dest/orchestrator/orchestrator.d.ts.map +1 -1
- package/dest/orchestrator/orchestrator.js +85 -74
- package/dest/orchestrator/orchestrator_metrics.d.ts.map +1 -1
- package/dest/orchestrator/orchestrator_metrics.js +2 -5
- package/dest/orchestrator/tx-proving-state.d.ts +0 -1
- package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/tx-proving-state.js +2 -34
- package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
- package/dest/prover-agent/memory-proving-queue.js +5 -4
- package/dest/prover-agent/prover-agent.d.ts.map +1 -1
- package/dest/prover-agent/prover-agent.js +3 -3
- package/dest/prover-client/factory.d.ts +6 -0
- package/dest/prover-client/factory.d.ts.map +1 -0
- package/dest/prover-client/factory.js +6 -0
- package/dest/prover-client/index.d.ts +3 -0
- package/dest/prover-client/index.d.ts.map +1 -0
- package/dest/prover-client/index.js +3 -0
- package/dest/{tx-prover/tx-prover.d.ts → prover-client/prover-client.d.ts} +8 -11
- package/dest/prover-client/prover-client.d.ts.map +1 -0
- package/dest/prover-client/prover-client.js +107 -0
- package/dest/proving_broker/caching_broker_facade.d.ts +12 -12
- package/dest/proving_broker/caching_broker_facade.d.ts.map +1 -1
- package/dest/proving_broker/caching_broker_facade.js +32 -29
- package/dest/proving_broker/factory.d.ts +2 -1
- package/dest/proving_broker/factory.d.ts.map +1 -1
- package/dest/proving_broker/factory.js +4 -4
- package/dest/proving_broker/proving_agent.d.ts +5 -0
- package/dest/proving_broker/proving_agent.d.ts.map +1 -1
- package/dest/proving_broker/proving_agent.js +15 -4
- package/dest/proving_broker/proving_agent_instrumentation.d.ts +8 -0
- package/dest/proving_broker/proving_agent_instrumentation.d.ts.map +1 -0
- package/dest/proving_broker/proving_agent_instrumentation.js +16 -0
- package/dest/proving_broker/proving_broker.d.ts +29 -5
- package/dest/proving_broker/proving_broker.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker.js +142 -41
- package/dest/proving_broker/proving_broker_database/persisted.d.ts +3 -1
- package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker_database/persisted.js +6 -2
- package/dest/proving_broker/proving_broker_instrumentation.d.ts +25 -0
- package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_instrumentation.js +85 -0
- package/dest/proving_broker/rpc.d.ts.map +1 -1
- package/dest/proving_broker/rpc.js +3 -2
- package/dest/test/mock_prover.d.ts +3 -2
- package/dest/test/mock_prover.d.ts.map +1 -1
- package/dest/test/mock_prover.js +9 -5
- package/package.json +18 -13
- package/src/block_builder/index.ts +6 -0
- package/src/block_builder/light.ts +120 -0
- package/src/index.ts +1 -2
- package/src/mocks/fixtures.ts +6 -18
- package/src/mocks/test_context.ts +85 -29
- package/src/orchestrator/block-building-helpers.ts +13 -14
- package/src/orchestrator/epoch-proving-state.ts +10 -13
- package/src/orchestrator/orchestrator.ts +101 -81
- package/src/orchestrator/orchestrator_metrics.ts +1 -11
- package/src/orchestrator/tx-proving-state.ts +1 -56
- package/src/prover-agent/memory-proving-queue.ts +4 -3
- package/src/prover-agent/prover-agent.ts +2 -2
- package/src/{tx-prover → prover-client}/factory.ts +4 -3
- package/src/prover-client/index.ts +2 -0
- package/src/{tx-prover/tx-prover.ts → prover-client/prover-client.ts} +25 -15
- package/src/proving_broker/caching_broker_facade.ts +31 -15
- package/src/proving_broker/factory.ts +7 -3
- package/src/proving_broker/proving_agent.ts +18 -3
- package/src/proving_broker/proving_agent_instrumentation.ts +21 -0
- package/src/proving_broker/proving_broker.ts +182 -50
- package/src/proving_broker/proving_broker_database/persisted.ts +11 -2
- package/src/proving_broker/proving_broker_instrumentation.ts +123 -0
- package/src/proving_broker/rpc.ts +2 -1
- package/src/test/mock_prover.ts +8 -4
- package/dest/tx-prover/factory.d.ts +0 -6
- package/dest/tx-prover/factory.d.ts.map +0 -1
- package/dest/tx-prover/factory.js +0 -6
- package/dest/tx-prover/tx-prover.d.ts.map +0 -1
- package/dest/tx-prover/tx-prover.js +0 -110
|
@@ -9,13 +9,17 @@ import {
|
|
|
9
9
|
type ProvingJobStatus,
|
|
10
10
|
ProvingRequestType,
|
|
11
11
|
} from '@aztec/circuit-types';
|
|
12
|
-
import {
|
|
12
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
13
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
13
14
|
import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
14
15
|
import { PriorityMemoryQueue } from '@aztec/foundation/queue';
|
|
16
|
+
import { Timer } from '@aztec/foundation/timer';
|
|
17
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
15
18
|
|
|
16
19
|
import assert from 'assert';
|
|
17
20
|
|
|
18
21
|
import { type ProvingBrokerDatabase } from './proving_broker_database.js';
|
|
22
|
+
import { type MonitorCallback, ProvingBrokerInstrumentation } from './proving_broker_instrumentation.js';
|
|
19
23
|
|
|
20
24
|
type InProgressMetadata = {
|
|
21
25
|
id: ProvingJobId;
|
|
@@ -27,29 +31,33 @@ type ProofRequestBrokerConfig = {
|
|
|
27
31
|
timeoutIntervalMs?: number;
|
|
28
32
|
jobTimeoutMs?: number;
|
|
29
33
|
maxRetries?: number;
|
|
34
|
+
maxEpochsToKeepResultsFor?: number;
|
|
35
|
+
maxParallelCleanUps?: number;
|
|
30
36
|
};
|
|
31
37
|
|
|
38
|
+
type EnqueuedProvingJob = Pick<ProvingJob, 'id' | 'epochNumber'>;
|
|
39
|
+
|
|
32
40
|
/**
|
|
33
41
|
* A broker that manages proof requests and distributes them to workers based on their priority.
|
|
34
42
|
* It takes a backend that is responsible for storing and retrieving proof requests and results.
|
|
35
43
|
*/
|
|
36
44
|
export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
37
45
|
private queues: ProvingQueues = {
|
|
38
|
-
[ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<
|
|
39
|
-
[ProvingRequestType.TUBE_PROOF]: new PriorityMemoryQueue<
|
|
40
|
-
[ProvingRequestType.PRIVATE_KERNEL_EMPTY]: new PriorityMemoryQueue<
|
|
46
|
+
[ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
47
|
+
[ProvingRequestType.TUBE_PROOF]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
48
|
+
[ProvingRequestType.PRIVATE_KERNEL_EMPTY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
41
49
|
|
|
42
|
-
[ProvingRequestType.PRIVATE_BASE_ROLLUP]: new PriorityMemoryQueue<
|
|
43
|
-
[ProvingRequestType.PUBLIC_BASE_ROLLUP]: new PriorityMemoryQueue<
|
|
44
|
-
[ProvingRequestType.MERGE_ROLLUP]: new PriorityMemoryQueue<
|
|
45
|
-
[ProvingRequestType.ROOT_ROLLUP]: new PriorityMemoryQueue<
|
|
50
|
+
[ProvingRequestType.PRIVATE_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
51
|
+
[ProvingRequestType.PUBLIC_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
52
|
+
[ProvingRequestType.MERGE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
53
|
+
[ProvingRequestType.ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
46
54
|
|
|
47
|
-
[ProvingRequestType.BLOCK_MERGE_ROLLUP]: new PriorityMemoryQueue<
|
|
48
|
-
[ProvingRequestType.BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<
|
|
49
|
-
[ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<
|
|
55
|
+
[ProvingRequestType.BLOCK_MERGE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
56
|
+
[ProvingRequestType.BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
57
|
+
[ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
50
58
|
|
|
51
|
-
[ProvingRequestType.BASE_PARITY]: new PriorityMemoryQueue<
|
|
52
|
-
[ProvingRequestType.ROOT_PARITY]: new PriorityMemoryQueue<
|
|
59
|
+
[ProvingRequestType.BASE_PARITY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
60
|
+
[ProvingRequestType.ROOT_PARITY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
53
61
|
};
|
|
54
62
|
|
|
55
63
|
// holds a copy of the database in memory in order to quickly fulfill requests
|
|
@@ -58,6 +66,9 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
58
66
|
// as above, but for results
|
|
59
67
|
private resultsCache = new Map<ProvingJobId, ProvingJobSettledResult>();
|
|
60
68
|
|
|
69
|
+
// tracks when each job was enqueued
|
|
70
|
+
private enqueuedAt = new Map<ProvingJobId, Timer>();
|
|
71
|
+
|
|
61
72
|
// keeps track of which jobs are currently being processed
|
|
62
73
|
// in the event of a crash this information is lost, but that's ok
|
|
63
74
|
// the next time the broker starts it will recreate jobsCache and still
|
|
@@ -70,23 +81,65 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
70
81
|
// a map of promises that will be resolved when a job is settled
|
|
71
82
|
private promises = new Map<ProvingJobId, PromiseWithResolvers<ProvingJobSettledResult>>();
|
|
72
83
|
|
|
73
|
-
private
|
|
74
|
-
private
|
|
84
|
+
private cleanupPromise: RunningPromise;
|
|
85
|
+
private msTimeSource = () => Date.now();
|
|
75
86
|
private jobTimeoutMs: number;
|
|
76
87
|
private maxRetries: number;
|
|
77
88
|
|
|
89
|
+
private instrumentation: ProvingBrokerInstrumentation;
|
|
90
|
+
|
|
91
|
+
private maxParallelCleanUps: number;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The broker keeps track of the highest epoch its seen.
|
|
95
|
+
* This information is used for garbage collection: once it reaches the next epoch, it can start pruning the database of old state.
|
|
96
|
+
* This clean up pass is only done against _settled_ jobs. This pass will not cancel jobs that are in-progress or in-queue.
|
|
97
|
+
* It is a client responsibility to cancel jobs if they are no longer necessary.
|
|
98
|
+
* Example:
|
|
99
|
+
* proving epoch 11 - the broker will wipe all setlled jobs for epochs 9 and lower
|
|
100
|
+
* finished proving epoch 11 and got first job for epoch 12 -> the broker will wipe all setlled jobs for epochs 10 and lower
|
|
101
|
+
* reorged back to end of epoch 10 -> epoch 11 is skipped and epoch 12 starts -> the broker will wipe all setlled jobs for epochs 10 and lower
|
|
102
|
+
*/
|
|
103
|
+
private epochHeight = 0;
|
|
104
|
+
private maxEpochsToKeepResultsFor = 1;
|
|
105
|
+
|
|
78
106
|
public constructor(
|
|
79
107
|
private database: ProvingBrokerDatabase,
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
client: TelemetryClient,
|
|
109
|
+
{
|
|
110
|
+
jobTimeoutMs = 30_000,
|
|
111
|
+
timeoutIntervalMs = 10_000,
|
|
112
|
+
maxRetries = 3,
|
|
113
|
+
maxEpochsToKeepResultsFor = 1,
|
|
114
|
+
maxParallelCleanUps = 20,
|
|
115
|
+
}: ProofRequestBrokerConfig = {},
|
|
116
|
+
private logger = createLogger('prover-client:proving-broker'),
|
|
82
117
|
) {
|
|
83
|
-
this.
|
|
118
|
+
this.instrumentation = new ProvingBrokerInstrumentation(client);
|
|
119
|
+
this.cleanupPromise = new RunningPromise(this.cleanupPass, timeoutIntervalMs);
|
|
84
120
|
this.jobTimeoutMs = jobTimeoutMs;
|
|
85
121
|
this.maxRetries = maxRetries;
|
|
122
|
+
this.maxEpochsToKeepResultsFor = maxEpochsToKeepResultsFor;
|
|
123
|
+
this.maxParallelCleanUps = maxParallelCleanUps;
|
|
86
124
|
}
|
|
87
125
|
|
|
88
|
-
|
|
89
|
-
|
|
126
|
+
private measureQueueDepth: MonitorCallback = (type: ProvingRequestType) => {
|
|
127
|
+
return this.queues[type].length();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
private countActiveJobs: MonitorCallback = (type: ProvingRequestType) => {
|
|
131
|
+
let count = 0;
|
|
132
|
+
for (const { id } of this.inProgress.values()) {
|
|
133
|
+
const job = this.jobsCache.get(id);
|
|
134
|
+
if (job?.type === type) {
|
|
135
|
+
count++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return count;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
public start(): Promise<void> {
|
|
90
143
|
for (const [item, result] of this.database.allProvingJobs()) {
|
|
91
144
|
this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`);
|
|
92
145
|
|
|
@@ -102,11 +155,16 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
102
155
|
}
|
|
103
156
|
}
|
|
104
157
|
|
|
105
|
-
this.
|
|
158
|
+
this.cleanupPromise.start();
|
|
159
|
+
|
|
160
|
+
this.instrumentation.monitorQueueDepth(this.measureQueueDepth);
|
|
161
|
+
this.instrumentation.monitorActiveJobs(this.countActiveJobs);
|
|
162
|
+
|
|
163
|
+
return Promise.resolve();
|
|
106
164
|
}
|
|
107
165
|
|
|
108
|
-
public stop(): Promise<void> {
|
|
109
|
-
|
|
166
|
+
public async stop(): Promise<void> {
|
|
167
|
+
await this.cleanupPromise.stop();
|
|
110
168
|
}
|
|
111
169
|
|
|
112
170
|
public async enqueueProvingJob(job: ProvingJob): Promise<void> {
|
|
@@ -129,15 +187,22 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
129
187
|
return promiseWithResolvers.promise;
|
|
130
188
|
}
|
|
131
189
|
|
|
132
|
-
public async
|
|
133
|
-
this.logger.info(`Cancelling job id=${id}`);
|
|
134
|
-
await this.database.deleteProvingJobAndResult(id);
|
|
135
|
-
|
|
190
|
+
public async cancelProvingJob(id: ProvingJobId): Promise<void> {
|
|
136
191
|
// notify listeners of the cancellation
|
|
137
192
|
if (!this.resultsCache.has(id)) {
|
|
138
|
-
this.
|
|
193
|
+
this.logger.info(`Cancelling job id=${id}`);
|
|
194
|
+
await this.reportProvingJobError(id, 'Aborted', false);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public async cleanUpProvingJobState(id: ProvingJobId): Promise<void> {
|
|
199
|
+
if (!this.resultsCache.has(id)) {
|
|
200
|
+
this.logger.warn(`Can't cleanup busy proving job: id=${id}`);
|
|
201
|
+
return;
|
|
139
202
|
}
|
|
140
203
|
|
|
204
|
+
this.logger.debug(`Cleaning up state for job id=${id}`);
|
|
205
|
+
await this.database.deleteProvingJobAndResult(id);
|
|
141
206
|
this.jobsCache.delete(id);
|
|
142
207
|
this.promises.delete(id);
|
|
143
208
|
this.resultsCache.delete(id);
|
|
@@ -174,19 +239,24 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
174
239
|
|
|
175
240
|
for (const proofType of allowedProofs) {
|
|
176
241
|
const queue = this.queues[proofType];
|
|
177
|
-
let
|
|
242
|
+
let enqueuedJob: EnqueuedProvingJob | undefined;
|
|
178
243
|
// exhaust the queue and make sure we're not sending a job that's already in progress
|
|
179
244
|
// or has already been completed
|
|
180
245
|
// this can happen if the broker crashes and restarts
|
|
181
246
|
// it's possible agents will report progress or results for jobs that are in the queue (after the restart)
|
|
182
|
-
while ((
|
|
183
|
-
|
|
184
|
-
|
|
247
|
+
while ((enqueuedJob = queue.getImmediate())) {
|
|
248
|
+
const job = this.jobsCache.get(enqueuedJob.id);
|
|
249
|
+
if (job && !this.inProgress.has(enqueuedJob.id) && !this.resultsCache.has(enqueuedJob.id)) {
|
|
250
|
+
const time = this.msTimeSource();
|
|
185
251
|
this.inProgress.set(job.id, {
|
|
186
252
|
id: job.id,
|
|
187
253
|
startedAt: time,
|
|
188
254
|
lastUpdatedAt: time,
|
|
189
255
|
});
|
|
256
|
+
const enqueuedAt = this.enqueuedAt.get(job.id);
|
|
257
|
+
if (enqueuedAt) {
|
|
258
|
+
this.instrumentation.recordJobWait(job.type, enqueuedAt);
|
|
259
|
+
}
|
|
190
260
|
|
|
191
261
|
return { job, time };
|
|
192
262
|
}
|
|
@@ -212,15 +282,23 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
212
282
|
this.inProgress.delete(id);
|
|
213
283
|
}
|
|
214
284
|
|
|
215
|
-
if (
|
|
285
|
+
if (this.resultsCache.has(id)) {
|
|
286
|
+
this.logger.warn(`Proving job id=${id} already is already settled, ignoring error`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (retry && retries + 1 < this.maxRetries && !this.isJobStale(item)) {
|
|
216
291
|
this.logger.info(`Retrying proving job id=${id} type=${ProvingRequestType[item.type]} retry=${retries + 1}`);
|
|
217
292
|
this.retries.set(id, retries + 1);
|
|
218
293
|
this.enqueueJobInternal(item);
|
|
294
|
+
this.instrumentation.incRetriedJobs(item.type);
|
|
219
295
|
return;
|
|
220
296
|
}
|
|
221
297
|
|
|
222
|
-
this.logger.
|
|
223
|
-
`Marking proving job id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${
|
|
298
|
+
this.logger.warn(
|
|
299
|
+
`Marking proving job as failed id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${
|
|
300
|
+
retries + 1
|
|
301
|
+
} err=${err}`,
|
|
224
302
|
);
|
|
225
303
|
|
|
226
304
|
await this.database.setProvingJobError(id, err);
|
|
@@ -228,6 +306,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
228
306
|
const result: ProvingJobSettledResult = { status: 'rejected', reason: String(err) };
|
|
229
307
|
this.resultsCache.set(id, result);
|
|
230
308
|
this.promises.get(id)!.resolve(result);
|
|
309
|
+
this.instrumentation.incRejectedJobs(item.type);
|
|
310
|
+
if (info) {
|
|
311
|
+
const duration = this.msTimeSource() - info.startedAt;
|
|
312
|
+
this.instrumentation.recordJobDuration(item.type, duration);
|
|
313
|
+
}
|
|
231
314
|
}
|
|
232
315
|
|
|
233
316
|
reportProvingJobProgress(
|
|
@@ -241,8 +324,13 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
241
324
|
return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
|
|
242
325
|
}
|
|
243
326
|
|
|
327
|
+
if (this.resultsCache.has(id)) {
|
|
328
|
+
this.logger.warn(`Proving job id=${id} has already been completed`);
|
|
329
|
+
return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
|
|
330
|
+
}
|
|
331
|
+
|
|
244
332
|
const metadata = this.inProgress.get(id);
|
|
245
|
-
const now = this.
|
|
333
|
+
const now = this.msTimeSource();
|
|
246
334
|
if (!metadata) {
|
|
247
335
|
this.logger.warn(
|
|
248
336
|
`Proving job id=${id} type=${ProvingRequestType[job.type]} not found in the in-progress cache, adding it`,
|
|
@@ -253,7 +341,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
253
341
|
this.inProgress.set(id, {
|
|
254
342
|
id,
|
|
255
343
|
startedAt,
|
|
256
|
-
lastUpdatedAt: this.
|
|
344
|
+
lastUpdatedAt: this.msTimeSource(),
|
|
257
345
|
});
|
|
258
346
|
return Promise.resolve(undefined);
|
|
259
347
|
} else if (startedAt <= metadata.startedAt) {
|
|
@@ -294,6 +382,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
294
382
|
this.inProgress.delete(id);
|
|
295
383
|
}
|
|
296
384
|
|
|
385
|
+
if (this.resultsCache.has(id)) {
|
|
386
|
+
this.logger.warn(`Proving job id=${id} already settled, ignoring result`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
297
390
|
this.logger.debug(
|
|
298
391
|
`Proving job complete id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${retries + 1}`,
|
|
299
392
|
);
|
|
@@ -303,9 +396,34 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
303
396
|
const result: ProvingJobSettledResult = { status: 'fulfilled', value };
|
|
304
397
|
this.resultsCache.set(id, result);
|
|
305
398
|
this.promises.get(id)!.resolve(result);
|
|
399
|
+
this.instrumentation.incResolvedJobs(item.type);
|
|
306
400
|
}
|
|
307
401
|
|
|
308
|
-
private
|
|
402
|
+
private cleanupPass = async () => {
|
|
403
|
+
await this.cleanupStaleJobs();
|
|
404
|
+
await this.reEnqueueExpiredJobs();
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
private async cleanupStaleJobs() {
|
|
408
|
+
const jobIds = Array.from(this.jobsCache.keys());
|
|
409
|
+
const jobsToClean: ProvingJobId[] = [];
|
|
410
|
+
for (const id of jobIds) {
|
|
411
|
+
const job = this.jobsCache.get(id)!;
|
|
412
|
+
const isComplete = this.resultsCache.has(id);
|
|
413
|
+
if (isComplete && this.isJobStale(job)) {
|
|
414
|
+
jobsToClean.push(id);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (jobsToClean.length > 0) {
|
|
419
|
+
this.logger.info(`Cleaning up [${jobsToClean.join(',')}]`);
|
|
420
|
+
await asyncPool(this.maxParallelCleanUps, jobsToClean, async jobId => {
|
|
421
|
+
await this.cleanUpProvingJobState(jobId);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private async reEnqueueExpiredJobs() {
|
|
309
427
|
const inProgressEntries = Array.from(this.inProgress.entries());
|
|
310
428
|
for (const [id, metadata] of inProgressEntries) {
|
|
311
429
|
const item = this.jobsCache.get(id);
|
|
@@ -315,26 +433,42 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
|
|
|
315
433
|
continue;
|
|
316
434
|
}
|
|
317
435
|
|
|
318
|
-
const
|
|
436
|
+
const now = this.msTimeSource();
|
|
437
|
+
const msSinceLastUpdate = now - metadata.lastUpdatedAt;
|
|
319
438
|
if (msSinceLastUpdate >= this.jobTimeoutMs) {
|
|
320
|
-
this.
|
|
321
|
-
|
|
322
|
-
|
|
439
|
+
if (this.isJobStale(item)) {
|
|
440
|
+
// the job has timed out and it's also old, just cancel and move on
|
|
441
|
+
await this.cancelProvingJob(item.id);
|
|
442
|
+
} else {
|
|
443
|
+
this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`);
|
|
444
|
+
this.inProgress.delete(id);
|
|
445
|
+
this.enqueueJobInternal(item);
|
|
446
|
+
this.instrumentation.incTimedOutJobs(item.type);
|
|
447
|
+
}
|
|
323
448
|
}
|
|
324
449
|
}
|
|
325
|
-
}
|
|
450
|
+
}
|
|
326
451
|
|
|
327
452
|
private enqueueJobInternal(job: ProvingJob): void {
|
|
328
453
|
if (!this.promises.has(job.id)) {
|
|
329
454
|
this.promises.set(job.id, promiseWithResolvers());
|
|
330
455
|
}
|
|
331
|
-
this.queues[job.type].put(
|
|
456
|
+
this.queues[job.type].put({
|
|
457
|
+
epochNumber: job.epochNumber,
|
|
458
|
+
id: job.id,
|
|
459
|
+
});
|
|
460
|
+
this.enqueuedAt.set(job.id, new Timer());
|
|
461
|
+
this.epochHeight = Math.max(this.epochHeight, job.epochNumber);
|
|
332
462
|
this.logger.debug(`Enqueued new proving job id=${job.id}`);
|
|
333
463
|
}
|
|
464
|
+
|
|
465
|
+
private isJobStale(job: ProvingJob) {
|
|
466
|
+
return job.epochNumber < this.epochHeight - this.maxEpochsToKeepResultsFor;
|
|
467
|
+
}
|
|
334
468
|
}
|
|
335
469
|
|
|
336
470
|
type ProvingQueues = {
|
|
337
|
-
[K in ProvingRequestType]: PriorityMemoryQueue<
|
|
471
|
+
[K in ProvingRequestType]: PriorityMemoryQueue<EnqueuedProvingJob>;
|
|
338
472
|
};
|
|
339
473
|
|
|
340
474
|
/**
|
|
@@ -343,12 +477,10 @@ type ProvingQueues = {
|
|
|
343
477
|
* @param b - Another proving job
|
|
344
478
|
* @returns A number indicating the relative priority of the two proving jobs
|
|
345
479
|
*/
|
|
346
|
-
function provingJobComparator(a:
|
|
347
|
-
|
|
348
|
-
const bBlockNumber = b.blockNumber ?? 0;
|
|
349
|
-
if (aBlockNumber < bBlockNumber) {
|
|
480
|
+
function provingJobComparator(a: EnqueuedProvingJob, b: EnqueuedProvingJob): -1 | 0 | 1 {
|
|
481
|
+
if (a.epochNumber < b.epochNumber) {
|
|
350
482
|
return -1;
|
|
351
|
-
} else if (
|
|
483
|
+
} else if (a.epochNumber > b.epochNumber) {
|
|
352
484
|
return 1;
|
|
353
485
|
} else {
|
|
354
486
|
return 0;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { type ProofUri, ProvingJob, type ProvingJobId, ProvingJobSettledResult } from '@aztec/circuit-types';
|
|
2
2
|
import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
|
|
3
3
|
import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
|
|
4
|
+
import { Attributes, LmdbMetrics, type TelemetryClient } from '@aztec/telemetry-client';
|
|
4
5
|
|
|
5
6
|
import { type ProvingBrokerDatabase } from '../proving_broker_database.js';
|
|
6
7
|
|
|
7
8
|
export class KVBrokerDatabase implements ProvingBrokerDatabase {
|
|
8
9
|
private jobs: AztecMap<ProvingJobId, string>;
|
|
9
10
|
private jobResults: AztecMap<ProvingJobId, string>;
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
private metrics: LmdbMetrics;
|
|
12
|
+
|
|
13
|
+
constructor(private store: AztecKVStore, client: TelemetryClient) {
|
|
14
|
+
this.metrics = new LmdbMetrics(
|
|
15
|
+
client.getMeter('KVBrokerDatabase'),
|
|
16
|
+
{
|
|
17
|
+
[Attributes.DB_DATA_TYPE]: 'prover-broker',
|
|
18
|
+
},
|
|
19
|
+
() => store.estimateSize(),
|
|
20
|
+
);
|
|
12
21
|
this.jobs = store.openMap('proving_jobs');
|
|
13
22
|
this.jobResults = store.openMap('proving_job_results');
|
|
14
23
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ProvingRequestType } from '@aztec/circuit-types';
|
|
2
|
+
import { type Timer } from '@aztec/foundation/timer';
|
|
3
|
+
import {
|
|
4
|
+
Attributes,
|
|
5
|
+
type Histogram,
|
|
6
|
+
Metrics,
|
|
7
|
+
type ObservableGauge,
|
|
8
|
+
type ObservableResult,
|
|
9
|
+
type TelemetryClient,
|
|
10
|
+
type UpDownCounter,
|
|
11
|
+
ValueType,
|
|
12
|
+
} from '@aztec/telemetry-client';
|
|
13
|
+
|
|
14
|
+
export type MonitorCallback = (proofType: ProvingRequestType) => number;
|
|
15
|
+
|
|
16
|
+
export class ProvingBrokerInstrumentation {
|
|
17
|
+
private queueSize: ObservableGauge;
|
|
18
|
+
private activeJobs: ObservableGauge;
|
|
19
|
+
private resolvedJobs: UpDownCounter;
|
|
20
|
+
private rejectedJobs: UpDownCounter;
|
|
21
|
+
private timedOutJobs: UpDownCounter;
|
|
22
|
+
private jobWait: Histogram;
|
|
23
|
+
private jobDuration: Histogram;
|
|
24
|
+
private retriedJobs: UpDownCounter;
|
|
25
|
+
|
|
26
|
+
constructor(client: TelemetryClient, name = 'ProvingBroker') {
|
|
27
|
+
const meter = client.getMeter(name);
|
|
28
|
+
|
|
29
|
+
this.queueSize = meter.createObservableGauge(Metrics.PROVING_QUEUE_SIZE, {
|
|
30
|
+
valueType: ValueType.INT,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.activeJobs = meter.createObservableGauge(Metrics.PROVING_QUEUE_ACTIVE_JOBS, {
|
|
34
|
+
valueType: ValueType.INT,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.resolvedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_RESOLVED_JOBS, {
|
|
38
|
+
valueType: ValueType.INT,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
this.rejectedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_REJECTED_JOBS, {
|
|
42
|
+
valueType: ValueType.INT,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.retriedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_RETRIED_JOBS, {
|
|
46
|
+
valueType: ValueType.INT,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.timedOutJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_TIMED_OUT_JOBS, {
|
|
50
|
+
valueType: ValueType.INT,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.jobWait = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_WAIT, {
|
|
54
|
+
description: 'Records how long a job sits in the queue',
|
|
55
|
+
unit: 'ms',
|
|
56
|
+
valueType: ValueType.INT,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.jobDuration = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_DURATION, {
|
|
60
|
+
description: 'Records how long a job takes to complete',
|
|
61
|
+
unit: 'ms',
|
|
62
|
+
valueType: ValueType.INT,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
monitorQueueDepth(fn: MonitorCallback) {
|
|
67
|
+
this.queueSize.addCallback(obs => this.observe(obs, fn));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
monitorActiveJobs(fn: MonitorCallback) {
|
|
71
|
+
this.activeJobs.addCallback(obs => this.observe(obs, fn));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
incResolvedJobs(proofType: ProvingRequestType) {
|
|
75
|
+
this.resolvedJobs.add(1, {
|
|
76
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
incRejectedJobs(proofType: ProvingRequestType) {
|
|
81
|
+
this.rejectedJobs.add(1, {
|
|
82
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
incRetriedJobs(proofType: ProvingRequestType) {
|
|
87
|
+
this.retriedJobs.add(1, {
|
|
88
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
incTimedOutJobs(proofType: ProvingRequestType) {
|
|
93
|
+
this.timedOutJobs.add(1, {
|
|
94
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
recordJobWait(proofType: ProvingRequestType, msOrTimer: Timer | number) {
|
|
99
|
+
const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
|
|
100
|
+
this.jobWait.record(duration, {
|
|
101
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
recordJobDuration(proofType: ProvingRequestType, msOrTimer: Timer | number) {
|
|
106
|
+
const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
|
|
107
|
+
this.jobDuration.record(duration, {
|
|
108
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private observe(obs: ObservableResult, fn: MonitorCallback) {
|
|
113
|
+
for (const proofType of Object.values(ProvingRequestType)) {
|
|
114
|
+
// a type predicate for TypeScript to recognize that we're only iterating over enum values
|
|
115
|
+
if (typeof proofType !== 'number') {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
obs.observe(fn(proofType), {
|
|
119
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -28,7 +28,8 @@ const GetProvingJobResponse = z.object({
|
|
|
28
28
|
export const ProvingJobProducerSchema: ApiSchemaFor<ProvingJobProducer> = {
|
|
29
29
|
enqueueProvingJob: z.function().args(ProvingJob).returns(z.void()),
|
|
30
30
|
getProvingJobStatus: z.function().args(ProvingJobId).returns(ProvingJobStatus),
|
|
31
|
-
|
|
31
|
+
cleanUpProvingJobState: z.function().args(ProvingJobId).returns(z.void()),
|
|
32
|
+
cancelProvingJob: z.function().args(ProvingJobId).returns(z.void()),
|
|
32
33
|
waitForJobToSettle: z.function().args(ProvingJobId).returns(ProvingJobSettledResult),
|
|
33
34
|
};
|
|
34
35
|
|
package/src/test/mock_prover.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
makeRootRollupPublicInputs,
|
|
44
44
|
} from '@aztec/circuits.js/testing';
|
|
45
45
|
import { times } from '@aztec/foundation/collection';
|
|
46
|
+
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
46
47
|
|
|
47
48
|
import { InlineProofStore, type ProofStore } from '../proving_broker/proof_store.js';
|
|
48
49
|
import { ProvingAgent } from '../proving_broker/proving_agent.js';
|
|
@@ -50,7 +51,7 @@ import { ProvingBroker } from '../proving_broker/proving_broker.js';
|
|
|
50
51
|
import { InMemoryBrokerDatabase } from '../proving_broker/proving_broker_database/memory.js';
|
|
51
52
|
|
|
52
53
|
export class TestBroker implements ProvingJobProducer {
|
|
53
|
-
private broker = new ProvingBroker(new InMemoryBrokerDatabase());
|
|
54
|
+
private broker = new ProvingBroker(new InMemoryBrokerDatabase(), new NoopTelemetryClient());
|
|
54
55
|
private agents: ProvingAgent[];
|
|
55
56
|
|
|
56
57
|
constructor(
|
|
@@ -58,7 +59,7 @@ export class TestBroker implements ProvingJobProducer {
|
|
|
58
59
|
prover: ServerCircuitProver,
|
|
59
60
|
private proofStore: ProofStore = new InlineProofStore(),
|
|
60
61
|
) {
|
|
61
|
-
this.agents = times(agentCount, () => new ProvingAgent(this.broker, proofStore, prover));
|
|
62
|
+
this.agents = times(agentCount, () => new ProvingAgent(this.broker, proofStore, prover, new NoopTelemetryClient()));
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
public async start() {
|
|
@@ -81,8 +82,11 @@ export class TestBroker implements ProvingJobProducer {
|
|
|
81
82
|
getProvingJobStatus(id: ProvingJobId): Promise<ProvingJobStatus> {
|
|
82
83
|
return this.broker.getProvingJobStatus(id);
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
return this.broker.
|
|
85
|
+
cleanUpProvingJobState(id: ProvingJobId): Promise<void> {
|
|
86
|
+
return this.broker.cleanUpProvingJobState(id);
|
|
87
|
+
}
|
|
88
|
+
cancelProvingJob(id: string): Promise<void> {
|
|
89
|
+
return this.broker.cancelProvingJob(id);
|
|
86
90
|
}
|
|
87
91
|
waitForJobToSettle(id: ProvingJobId): Promise<ProvingJobSettledResult> {
|
|
88
92
|
return this.broker.waitForJobToSettle(id);
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type ProvingJobBroker } from '@aztec/circuit-types';
|
|
2
|
-
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
3
|
-
import { type ProverClientConfig } from '../config.js';
|
|
4
|
-
import { TxProver } from './tx-prover.js';
|
|
5
|
-
export declare function createProverClient(config: ProverClientConfig, broker: ProvingJobBroker, telemetry?: TelemetryClient): Promise<TxProver>;
|
|
6
|
-
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/tx-prover/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,gBAAgB,EACxB,SAAS,GAAE,eAA2C,qBAGvD"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
2
|
-
import { TxProver } from './tx-prover.js';
|
|
3
|
-
export function createProverClient(config, broker, telemetry = new NoopTelemetryClient()) {
|
|
4
|
-
return TxProver.new(config, broker, telemetry);
|
|
5
|
-
}
|
|
6
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eC1wcm92ZXIvZmFjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUduRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUMsTUFBTSxVQUFVLGtCQUFrQixDQUNoQyxNQUEwQixFQUMxQixNQUF3QixFQUN4QixZQUE2QixJQUFJLG1CQUFtQixFQUFFO0lBRXRELE9BQU8sUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQ2pELENBQUMifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tx-prover.d.ts","sourceRoot":"","sources":["../../src/tx-prover/tx-prover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA2C,MAAM,kBAAkB,CAAC;AAC3G,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EAEvB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAIxC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAOvD;;;GAGG;AACH,qBAAa,QAAS,YAAW,kBAAkB;IAO/C,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,GAAG;IAVb,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAsB;IAEpC,OAAO,CAAC,QAAQ,CAAC,CAAS;IAE1B,OAAO;IAYA,iBAAiB,CAAC,EAAE,EAAE,yBAAyB,EAAE,KAAK,GAAE,WAAuC,GAAG,WAAW;IAS7G,WAAW,IAAI,EAAE;IAIlB,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IASnC;;OAEG;IACU,IAAI;IAQjB;;;;;;OAMG;WACiB,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe;IAMjG,mBAAmB,IAAI,kBAAkB;YAQlC,oBAAoB;YAmBpB,UAAU;CAGzB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GAAG,UAAU,GAAG,QAAQ,EAClD,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAU9B"}
|