@aztec/prover-client 0.69.1 → 0.70.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.
Files changed (94) hide show
  1. package/dest/block_builder/light.d.ts +0 -1
  2. package/dest/block_builder/light.d.ts.map +1 -1
  3. package/dest/block_builder/light.js +3 -13
  4. package/dest/config.d.ts +2 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +3 -2
  7. package/dest/mocks/fixtures.d.ts +1 -1
  8. package/dest/mocks/fixtures.d.ts.map +1 -1
  9. package/dest/mocks/fixtures.js +2 -2
  10. package/dest/mocks/test_context.d.ts +1 -1
  11. package/dest/mocks/test_context.d.ts.map +1 -1
  12. package/dest/mocks/test_context.js +3 -3
  13. package/dest/orchestrator/block-building-helpers.d.ts +10 -25
  14. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  15. package/dest/orchestrator/block-building-helpers.js +34 -45
  16. package/dest/orchestrator/block-proving-state.d.ts +40 -44
  17. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  18. package/dest/orchestrator/block-proving-state.js +149 -85
  19. package/dest/orchestrator/epoch-proving-state.d.ts +23 -30
  20. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  21. package/dest/orchestrator/epoch-proving-state.js +92 -65
  22. package/dest/orchestrator/orchestrator.d.ts +16 -47
  23. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  24. package/dest/orchestrator/orchestrator.js +204 -341
  25. package/dest/orchestrator/tx-proving-state.d.ts +10 -6
  26. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  27. package/dest/orchestrator/tx-proving-state.js +57 -46
  28. package/dest/prover-agent/memory-proving-queue.d.ts +3 -3
  29. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  30. package/dest/prover-agent/memory-proving-queue.js +4 -4
  31. package/dest/prover-agent/prover-agent.js +4 -4
  32. package/dest/prover-client/prover-client.d.ts.map +1 -1
  33. package/dest/prover-client/prover-client.js +6 -3
  34. package/dest/prover-client/server-epoch-prover.d.ts +25 -0
  35. package/dest/prover-client/server-epoch-prover.d.ts.map +1 -0
  36. package/dest/prover-client/server-epoch-prover.js +40 -0
  37. package/dest/proving_broker/broker_prover_facade.d.ts +15 -4
  38. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
  39. package/dest/proving_broker/broker_prover_facade.js +247 -44
  40. package/dest/proving_broker/config.d.ts +61 -0
  41. package/dest/proving_broker/config.d.ts.map +1 -0
  42. package/dest/proving_broker/config.js +83 -0
  43. package/dest/proving_broker/factory.d.ts +1 -1
  44. package/dest/proving_broker/factory.d.ts.map +1 -1
  45. package/dest/proving_broker/factory.js +2 -5
  46. package/dest/proving_broker/fixtures.d.ts +5 -0
  47. package/dest/proving_broker/fixtures.d.ts.map +1 -0
  48. package/dest/proving_broker/fixtures.js +12 -0
  49. package/dest/proving_broker/index.d.ts +1 -0
  50. package/dest/proving_broker/index.d.ts.map +1 -1
  51. package/dest/proving_broker/index.js +2 -1
  52. package/dest/proving_broker/proving_broker.d.ts +16 -12
  53. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  54. package/dest/proving_broker/proving_broker.js +306 -273
  55. package/dest/proving_broker/proving_broker_database/memory.d.ts +4 -2
  56. package/dest/proving_broker/proving_broker_database/memory.d.ts.map +1 -1
  57. package/dest/proving_broker/proving_broker_database/memory.js +17 -4
  58. package/dest/proving_broker/proving_broker_database/persisted.d.ts +10 -6
  59. package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
  60. package/dest/proving_broker/proving_broker_database/persisted.js +105 -13
  61. package/dest/proving_broker/proving_broker_database.d.ts +7 -3
  62. package/dest/proving_broker/proving_broker_database.d.ts.map +1 -1
  63. package/dest/proving_broker/proving_job_controller.js +4 -4
  64. package/dest/proving_broker/rpc.d.ts.map +1 -1
  65. package/dest/proving_broker/rpc.js +4 -4
  66. package/dest/test/mock_prover.d.ts +7 -7
  67. package/dest/test/mock_prover.d.ts.map +1 -1
  68. package/dest/test/mock_prover.js +6 -6
  69. package/package.json +11 -11
  70. package/src/block_builder/light.ts +1 -20
  71. package/src/config.ts +4 -4
  72. package/src/mocks/fixtures.ts +1 -1
  73. package/src/mocks/test_context.ts +2 -2
  74. package/src/orchestrator/block-building-helpers.ts +45 -119
  75. package/src/orchestrator/block-proving-state.ts +251 -121
  76. package/src/orchestrator/epoch-proving-state.ts +159 -88
  77. package/src/orchestrator/orchestrator.ts +251 -527
  78. package/src/orchestrator/tx-proving-state.ts +30 -18
  79. package/src/prover-agent/memory-proving-queue.ts +11 -15
  80. package/src/prover-agent/prover-agent.ts +4 -4
  81. package/src/prover-client/prover-client.ts +5 -7
  82. package/src/prover-client/server-epoch-prover.ts +44 -0
  83. package/src/proving_broker/broker_prover_facade.ts +321 -64
  84. package/src/proving_broker/config.ts +93 -0
  85. package/src/proving_broker/factory.ts +2 -5
  86. package/src/proving_broker/fixtures.ts +14 -0
  87. package/src/proving_broker/index.ts +1 -0
  88. package/src/proving_broker/proving_broker.ts +114 -71
  89. package/src/proving_broker/proving_broker_database/memory.ts +24 -4
  90. package/src/proving_broker/proving_broker_database/persisted.ts +141 -19
  91. package/src/proving_broker/proving_broker_database.ts +8 -3
  92. package/src/proving_broker/proving_job_controller.ts +5 -5
  93. package/src/proving_broker/rpc.ts +2 -3
  94. package/src/test/mock_prover.ts +9 -14
@@ -9,10 +9,9 @@ import {
9
9
  type ProvingJobStatus,
10
10
  ProvingRequestType,
11
11
  } from '@aztec/circuit-types';
12
- import { asyncPool } from '@aztec/foundation/async-pool';
13
12
  import { createLogger } from '@aztec/foundation/log';
14
13
  import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
15
- import { PriorityMemoryQueue } from '@aztec/foundation/queue';
14
+ import { PriorityMemoryQueue, SerialQueue } from '@aztec/foundation/queue';
16
15
  import { Timer } from '@aztec/foundation/timer';
17
16
  import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
18
17
 
@@ -45,7 +44,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
45
44
  private queues: ProvingQueues = {
46
45
  [ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
47
46
  [ProvingRequestType.TUBE_PROOF]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
48
- [ProvingRequestType.PRIVATE_KERNEL_EMPTY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
49
47
 
50
48
  [ProvingRequestType.PRIVATE_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
51
49
  [ProvingRequestType.PUBLIC_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
@@ -54,6 +52,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
54
52
 
55
53
  [ProvingRequestType.BLOCK_MERGE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
56
54
  [ProvingRequestType.BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
55
+ [ProvingRequestType.SINGLE_TX_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
57
56
  [ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
58
57
 
59
58
  [ProvingRequestType.BASE_PARITY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
@@ -89,21 +88,24 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
89
88
  private instrumentation: ProvingBrokerInstrumentation;
90
89
  public readonly tracer: Tracer;
91
90
 
92
- private maxParallelCleanUps: number;
91
+ private completedJobNotifications: ProvingJobId[] = [];
93
92
 
94
93
  /**
95
94
  * The broker keeps track of the highest epoch its seen.
96
95
  * This information is used for garbage collection: once it reaches the next epoch, it can start pruning the database of old state.
97
- * This clean up pass is only done against _settled_ jobs. This pass will not cancel jobs that are in-progress or in-queue.
98
- * It is a client responsibility to cancel jobs if they are no longer necessary.
96
+ * It is important that this value is initialised to zero. This ensures that we don't delete any old jobs until the current
97
+ * process instance receives a job request informing it of the actual current highest epoch
99
98
  * Example:
100
- * proving epoch 11 - the broker will wipe all setlled jobs for epochs 9 and lower
101
- * finished proving epoch 11 and got first job for epoch 12 -> the broker will wipe all setlled jobs for epochs 10 and lower
102
- * 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
99
+ * proving epoch 11 - the broker will wipe all jobs for epochs 9 and lower
100
+ * finished proving epoch 11 and got first job for epoch 12 -> the broker will wipe all settled 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 settled jobs for epochs 10 and lower
103
102
  */
104
103
  private epochHeight = 0;
105
104
  private maxEpochsToKeepResultsFor = 1;
106
105
 
106
+ private requestQueue: SerialQueue = new SerialQueue();
107
+ private started = false;
108
+
107
109
  public constructor(
108
110
  private database: ProvingBrokerDatabase,
109
111
  client: TelemetryClient,
@@ -112,7 +114,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
112
114
  timeoutIntervalMs = 10_000,
113
115
  maxRetries = 3,
114
116
  maxEpochsToKeepResultsFor = 1,
115
- maxParallelCleanUps = 20,
116
117
  }: ProofRequestBrokerConfig = {},
117
118
  private logger = createLogger('prover-client:proving-broker'),
118
119
  ) {
@@ -122,7 +123,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
122
123
  this.jobTimeoutMs = jobTimeoutMs;
123
124
  this.maxRetries = maxRetries;
124
125
  this.maxEpochsToKeepResultsFor = maxEpochsToKeepResultsFor;
125
- this.maxParallelCleanUps = maxParallelCleanUps;
126
126
  }
127
127
 
128
128
  private measureQueueDepth: MonitorCallback = (type: ProvingRequestType) => {
@@ -142,6 +142,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
142
142
  };
143
143
 
144
144
  public start(): Promise<void> {
145
+ if (this.started) {
146
+ this.logger.info('Proving Broker already started');
147
+ return Promise.resolve();
148
+ }
149
+ this.logger.info('Proving Broker started');
145
150
  for (const [item, result] of this.database.allProvingJobs()) {
146
151
  this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`, {
147
152
  provingJobId: item.id,
@@ -161,24 +166,71 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
161
166
 
162
167
  this.cleanupPromise.start();
163
168
 
169
+ this.requestQueue.start();
170
+
164
171
  this.instrumentation.monitorQueueDepth(this.measureQueueDepth);
165
172
  this.instrumentation.monitorActiveJobs(this.countActiveJobs);
166
173
 
174
+ this.started = true;
175
+
167
176
  return Promise.resolve();
168
177
  }
169
178
 
170
179
  public async stop(): Promise<void> {
180
+ if (!this.started) {
181
+ this.logger.warn('ProvingBroker not started');
182
+ return Promise.resolve();
183
+ }
184
+ await this.requestQueue.cancel();
171
185
  await this.cleanupPromise.stop();
172
186
  }
173
187
 
174
- public async enqueueProvingJob(job: ProvingJob): Promise<void> {
188
+ public enqueueProvingJob(job: ProvingJob): Promise<ProvingJobStatus> {
189
+ return this.requestQueue.put(() => this.#enqueueProvingJob(job));
190
+ }
191
+
192
+ public cancelProvingJob(id: ProvingJobId): Promise<void> {
193
+ return this.requestQueue.put(() => this.#cancelProvingJob(id));
194
+ }
195
+
196
+ public getProvingJobStatus(id: ProvingJobId): Promise<ProvingJobStatus> {
197
+ return this.requestQueue.put(() => this.#getProvingJobStatus(id));
198
+ }
199
+
200
+ public getCompletedJobs(ids: ProvingJobId[]): Promise<ProvingJobId[]> {
201
+ return this.requestQueue.put(() => this.#getCompletedJobs(ids));
202
+ }
203
+
204
+ public getProvingJob(filter?: ProvingJobFilter): Promise<{ job: ProvingJob; time: number } | undefined> {
205
+ return this.requestQueue.put(() => this.#getProvingJob(filter));
206
+ }
207
+
208
+ public reportProvingJobSuccess(id: ProvingJobId, value: ProofUri): Promise<void> {
209
+ return this.requestQueue.put(() => this.#reportProvingJobSuccess(id, value));
210
+ }
211
+
212
+ public reportProvingJobError(id: ProvingJobId, err: string, retry = false): Promise<void> {
213
+ return this.requestQueue.put(() => this.#reportProvingJobError(id, err, retry));
214
+ }
215
+
216
+ public reportProvingJobProgress(
217
+ id: ProvingJobId,
218
+ startedAt: number,
219
+ filter?: ProvingJobFilter,
220
+ ): Promise<{ job: ProvingJob; time: number } | undefined> {
221
+ return this.requestQueue.put(() => this.#reportProvingJobProgress(id, startedAt, filter));
222
+ }
223
+
224
+ async #enqueueProvingJob(job: ProvingJob): Promise<ProvingJobStatus> {
225
+ // We return the job status at the start of this call
226
+ const jobStatus = await this.#getProvingJobStatus(job.id);
175
227
  if (this.jobsCache.has(job.id)) {
176
228
  const existing = this.jobsCache.get(job.id);
177
229
  assert.deepStrictEqual(job, existing, 'Duplicate proving job ID');
178
230
  this.logger.debug(`Duplicate proving job id=${job.id} epochNumber=${job.epochNumber}. Ignoring`, {
179
231
  provingJobId: job.id,
180
232
  });
181
- return;
233
+ return jobStatus;
182
234
  }
183
235
 
184
236
  if (this.isJobStale(job)) {
@@ -199,18 +251,10 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
199
251
  this.jobsCache.delete(job.id);
200
252
  throw err;
201
253
  }
254
+ return jobStatus;
202
255
  }
203
256
 
204
- public waitForJobToSettle(id: ProvingJobId): Promise<ProvingJobSettledResult> {
205
- const promiseWithResolvers = this.promises.get(id);
206
- if (!promiseWithResolvers) {
207
- this.logger.warn(`Job id=${id} not found`, { provingJobId: id });
208
- return Promise.resolve({ status: 'rejected', reason: `Job ${id} not found` });
209
- }
210
- return promiseWithResolvers.promise;
211
- }
212
-
213
- public async cancelProvingJob(id: ProvingJobId): Promise<void> {
257
+ async #cancelProvingJob(id: ProvingJobId): Promise<void> {
214
258
  if (!this.jobsCache.has(id)) {
215
259
  this.logger.warn(`Can't cancel a job that doesn't exist id=${id}`, { provingJobId: id });
216
260
  return;
@@ -219,31 +263,21 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
219
263
  // notify listeners of the cancellation
220
264
  if (!this.resultsCache.has(id)) {
221
265
  this.logger.info(`Cancelling job id=${id}`, { provingJobId: id });
222
- await this.reportProvingJobError(id, 'Aborted', false);
266
+ await this.#reportProvingJobError(id, 'Aborted', false);
223
267
  }
224
268
  }
225
269
 
226
- private async cleanUpProvingJobState(id: ProvingJobId): Promise<void> {
227
- if (!this.jobsCache.has(id)) {
228
- this.logger.warn(`Can't clean up a job that doesn't exist id=${id}`, { provingJobId: id });
229
- return;
230
- }
231
-
232
- if (!this.resultsCache.has(id)) {
233
- this.logger.warn(`Can't cleanup busy proving job: id=${id}`, { provingJobId: id });
234
- return;
270
+ private cleanUpProvingJobState(ids: ProvingJobId[]) {
271
+ for (const id of ids) {
272
+ this.jobsCache.delete(id);
273
+ this.promises.delete(id);
274
+ this.resultsCache.delete(id);
275
+ this.inProgress.delete(id);
276
+ this.retries.delete(id);
235
277
  }
236
-
237
- this.logger.debug(`Cleaning up state for job id=${id}`, { provingJobId: id });
238
- await this.database.deleteProvingJobAndResult(id);
239
- this.jobsCache.delete(id);
240
- this.promises.delete(id);
241
- this.resultsCache.delete(id);
242
- this.inProgress.delete(id);
243
- this.retries.delete(id);
244
278
  }
245
279
 
246
- public getProvingJobStatus(id: ProvingJobId): Promise<ProvingJobStatus> {
280
+ #getProvingJobStatus(id: ProvingJobId): Promise<ProvingJobStatus> {
247
281
  const result = this.resultsCache.get(id);
248
282
  if (result) {
249
283
  return Promise.resolve(result);
@@ -252,7 +286,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
252
286
  const item = this.jobsCache.get(id);
253
287
 
254
288
  if (!item) {
255
- this.logger.warn(`Proving job id=${id} not found`, { provingJobId: id });
256
289
  return Promise.resolve({ status: 'not-found' });
257
290
  }
258
291
 
@@ -260,8 +293,15 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
260
293
  }
261
294
  }
262
295
 
296
+ #getCompletedJobs(ids: ProvingJobId[]): Promise<ProvingJobId[]> {
297
+ const completedJobs = ids.filter(id => this.resultsCache.has(id));
298
+ const notifications = this.completedJobNotifications;
299
+ this.completedJobNotifications = [];
300
+ return Promise.resolve(notifications.concat(completedJobs));
301
+ }
302
+
263
303
  // eslint-disable-next-line require-await
264
- async getProvingJob(
304
+ async #getProvingJob(
265
305
  filter: ProvingJobFilter = { allowList: [] },
266
306
  ): Promise<{ job: ProvingJob; time: number } | undefined> {
267
307
  const allowedProofs: ProvingRequestType[] =
@@ -299,7 +339,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
299
339
  return undefined;
300
340
  }
301
341
 
302
- async reportProvingJobError(id: ProvingJobId, err: string, retry = false): Promise<void> {
342
+ async #reportProvingJobError(id: ProvingJobId, err: string, retry = false): Promise<void> {
303
343
  const info = this.inProgress.get(id);
304
344
  const item = this.jobsCache.get(id);
305
345
  const retries = this.retries.get(id) ?? 0;
@@ -351,6 +391,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
351
391
  const result: ProvingJobSettledResult = { status: 'rejected', reason: String(err) };
352
392
  this.resultsCache.set(id, result);
353
393
  this.promises.get(id)!.resolve(result);
394
+ this.completedJobNotifications.push(id);
354
395
 
355
396
  this.instrumentation.incRejectedJobs(item.type);
356
397
  if (info) {
@@ -369,7 +410,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
369
410
  }
370
411
  }
371
412
 
372
- reportProvingJobProgress(
413
+ #reportProvingJobProgress(
373
414
  id: ProvingJobId,
374
415
  startedAt: number,
375
416
  filter?: ProvingJobFilter,
@@ -377,12 +418,12 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
377
418
  const job = this.jobsCache.get(id);
378
419
  if (!job) {
379
420
  this.logger.warn(`Proving job id=${id} does not exist`, { provingJobId: id });
380
- return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
421
+ return filter ? this.#getProvingJob(filter) : Promise.resolve(undefined);
381
422
  }
382
423
 
383
424
  if (this.resultsCache.has(id)) {
384
425
  this.logger.warn(`Proving job id=${id} has already been completed`, { provingJobId: id });
385
- return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
426
+ return filter ? this.#getProvingJob(filter) : Promise.resolve(undefined);
386
427
  }
387
428
 
388
429
  const metadata = this.inProgress.get(id);
@@ -420,13 +461,13 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
420
461
  } already being worked on by another agent. Sending new one`,
421
462
  { provingJobId: id },
422
463
  );
423
- return this.getProvingJob(filter);
464
+ return this.#getProvingJob(filter);
424
465
  } else {
425
466
  return Promise.resolve(undefined);
426
467
  }
427
468
  }
428
469
 
429
- async reportProvingJobSuccess(id: ProvingJobId, value: ProofUri): Promise<void> {
470
+ async #reportProvingJobSuccess(id: ProvingJobId, value: ProofUri): Promise<void> {
430
471
  const info = this.inProgress.get(id);
431
472
  const item = this.jobsCache.get(id);
432
473
  const retries = this.retries.get(id) ?? 0;
@@ -459,6 +500,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
459
500
  const result: ProvingJobSettledResult = { status: 'fulfilled', value };
460
501
  this.resultsCache.set(id, result);
461
502
  this.promises.get(id)!.resolve(result);
503
+ this.completedJobNotifications.push(id);
462
504
 
463
505
  this.instrumentation.incResolvedJobs(item.type);
464
506
  if (info) {
@@ -479,30 +521,32 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
479
521
 
480
522
  @trackSpan('ProvingBroker.cleanupPass')
481
523
  private async cleanupPass() {
482
- await this.cleanupStaleJobs();
483
- await this.reEnqueueExpiredJobs();
524
+ this.cleanupStaleJobs();
525
+ this.reEnqueueExpiredJobs();
526
+ const oldestEpochToKeep = this.oldestEpochToKeep();
527
+ if (oldestEpochToKeep > 0) {
528
+ await this.requestQueue.put(() => this.database.deleteAllProvingJobsOlderThanEpoch(oldestEpochToKeep));
529
+ this.logger.trace(`Deleted all epochs older than ${oldestEpochToKeep}`);
530
+ }
484
531
  }
485
532
 
486
- private async cleanupStaleJobs() {
533
+ private cleanupStaleJobs() {
487
534
  const jobIds = Array.from(this.jobsCache.keys());
488
535
  const jobsToClean: ProvingJobId[] = [];
489
536
  for (const id of jobIds) {
490
537
  const job = this.jobsCache.get(id)!;
491
- const isComplete = this.resultsCache.has(id);
492
- if (isComplete && this.isJobStale(job)) {
538
+ if (this.isJobStale(job)) {
493
539
  jobsToClean.push(id);
494
540
  }
495
541
  }
496
542
 
497
543
  if (jobsToClean.length > 0) {
498
- this.logger.info(`Cleaning up jobs=${jobsToClean.length}`);
499
- await asyncPool(this.maxParallelCleanUps, jobsToClean, async jobId => {
500
- await this.cleanUpProvingJobState(jobId);
501
- });
544
+ this.cleanUpProvingJobState(jobsToClean);
545
+ this.logger.info(`Cleaned up jobs=${jobsToClean.length}`);
502
546
  }
503
547
  }
504
548
 
505
- private async reEnqueueExpiredJobs() {
549
+ private reEnqueueExpiredJobs() {
506
550
  const inProgressEntries = Array.from(this.inProgress.entries());
507
551
  for (const [id, metadata] of inProgressEntries) {
508
552
  const item = this.jobsCache.get(id);
@@ -515,15 +559,10 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
515
559
  const now = this.msTimeSource();
516
560
  const msSinceLastUpdate = now - metadata.lastUpdatedAt;
517
561
  if (msSinceLastUpdate >= this.jobTimeoutMs) {
518
- if (this.isJobStale(item)) {
519
- // the job has timed out and it's also old, just cancel and move on
520
- await this.cancelProvingJob(item.id);
521
- } else {
522
- this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`, { provingJobId: id });
523
- this.inProgress.delete(id);
524
- this.enqueueJobInternal(item);
525
- this.instrumentation.incTimedOutJobs(item.type);
526
- }
562
+ this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`, { provingJobId: id });
563
+ this.inProgress.delete(id);
564
+ this.enqueueJobInternal(item);
565
+ this.instrumentation.incTimedOutJobs(item.type);
527
566
  }
528
567
  }
529
568
  }
@@ -541,7 +580,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
541
580
  }
542
581
 
543
582
  private isJobStale(job: ProvingJob) {
544
- return job.epochNumber < this.epochHeight - this.maxEpochsToKeepResultsFor;
583
+ return job.epochNumber < this.oldestEpochToKeep();
584
+ }
585
+
586
+ private oldestEpochToKeep() {
587
+ return this.epochHeight - this.maxEpochsToKeepResultsFor;
545
588
  }
546
589
  }
547
590
 
@@ -601,6 +644,7 @@ function proofTypeComparator(a: ProvingRequestType, b: ProvingRequestType): -1 |
601
644
  */
602
645
  const PROOF_TYPES_IN_PRIORITY_ORDER: ProvingRequestType[] = [
603
646
  ProvingRequestType.BLOCK_ROOT_ROLLUP,
647
+ ProvingRequestType.SINGLE_TX_BLOCK_ROOT_ROLLUP,
604
648
  ProvingRequestType.BLOCK_MERGE_ROLLUP,
605
649
  ProvingRequestType.ROOT_ROLLUP,
606
650
  ProvingRequestType.MERGE_ROLLUP,
@@ -611,5 +655,4 @@ const PROOF_TYPES_IN_PRIORITY_ORDER: ProvingRequestType[] = [
611
655
  ProvingRequestType.ROOT_PARITY,
612
656
  ProvingRequestType.BASE_PARITY,
613
657
  ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP,
614
- ProvingRequestType.PRIVATE_KERNEL_EMPTY,
615
658
  ];
@@ -1,4 +1,10 @@
1
- import type { ProofUri, ProvingJob, ProvingJobId, ProvingJobSettledResult } from '@aztec/circuit-types';
1
+ import {
2
+ type ProofUri,
3
+ type ProvingJob,
4
+ type ProvingJobId,
5
+ type ProvingJobSettledResult,
6
+ getEpochFromProvingJobId,
7
+ } from '@aztec/circuit-types';
2
8
 
3
9
  import { type ProvingBrokerDatabase } from '../proving_broker_database.js';
4
10
 
@@ -29,15 +35,29 @@ export class InMemoryBrokerDatabase implements ProvingBrokerDatabase {
29
35
  return Promise.resolve();
30
36
  }
31
37
 
32
- deleteProvingJobAndResult(id: ProvingJobId): Promise<void> {
33
- this.jobs.delete(id);
34
- this.results.delete(id);
38
+ deleteProvingJobs(ids: ProvingJobId[]): Promise<void> {
39
+ for (const id of ids) {
40
+ this.jobs.delete(id);
41
+ this.results.delete(id);
42
+ }
35
43
  return Promise.resolve();
36
44
  }
37
45
 
46
+ deleteAllProvingJobsOlderThanEpoch(epochNumber: number): Promise<void> {
47
+ const toDelete = [
48
+ ...Array.from(this.jobs.keys()).filter(x => getEpochFromProvingJobId(x) < epochNumber),
49
+ ...Array.from(this.results.keys()).filter(x => getEpochFromProvingJobId(x) < epochNumber),
50
+ ];
51
+ return this.deleteProvingJobs(toDelete);
52
+ }
53
+
38
54
  *allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]> {
39
55
  for (const item of this.jobs.values()) {
40
56
  yield [item, this.results.get(item.id)] as const;
41
57
  }
42
58
  }
59
+
60
+ close(): Promise<void> {
61
+ return Promise.resolve();
62
+ }
43
63
  }
@@ -1,27 +1,35 @@
1
- import { type ProofUri, ProvingJob, type ProvingJobId, ProvingJobSettledResult } from '@aztec/circuit-types';
1
+ import {
2
+ type ProofUri,
3
+ ProvingJob,
4
+ type ProvingJobId,
5
+ ProvingJobSettledResult,
6
+ getEpochFromProvingJobId,
7
+ } from '@aztec/circuit-types';
2
8
  import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
3
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
9
+ import { type Logger, createLogger } from '@aztec/foundation/log';
10
+ import { type AztecMap } from '@aztec/kv-store';
11
+ import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
4
12
  import { Attributes, LmdbMetrics, type TelemetryClient } from '@aztec/telemetry-client';
5
13
 
14
+ import { mkdir, readdir } from 'fs/promises';
15
+ import { join } from 'path';
16
+
17
+ import { type ProverBrokerConfig } from '../config.js';
6
18
  import { type ProvingBrokerDatabase } from '../proving_broker_database.js';
7
19
 
8
- export class KVBrokerDatabase implements ProvingBrokerDatabase {
20
+ class SingleEpochDatabase {
9
21
  private jobs: AztecMap<ProvingJobId, string>;
10
22
  private jobResults: AztecMap<ProvingJobId, string>;
11
- private metrics: LmdbMetrics;
12
23
 
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
- );
24
+ constructor(public readonly store: AztecLmdbStore) {
21
25
  this.jobs = store.openMap('proving_jobs');
22
26
  this.jobResults = store.openMap('proving_job_results');
23
27
  }
24
28
 
29
+ estimateSize() {
30
+ return this.store.estimateSize();
31
+ }
32
+
25
33
  async addProvingJob(job: ProvingJob): Promise<void> {
26
34
  await this.jobs.set(job.id, jsonStringify(job));
27
35
  }
@@ -35,13 +43,6 @@ export class KVBrokerDatabase implements ProvingBrokerDatabase {
35
43
  }
36
44
  }
37
45
 
38
- deleteProvingJobAndResult(id: ProvingJobId): Promise<void> {
39
- return this.store.transaction(() => {
40
- void this.jobs.delete(id);
41
- void this.jobResults.delete(id);
42
- });
43
- }
44
-
45
46
  async setProvingJobError(id: ProvingJobId, reason: string): Promise<void> {
46
47
  const result: ProvingJobSettledResult = { status: 'rejected', reason };
47
48
  await this.jobResults.set(id, jsonStringify(result));
@@ -51,4 +52,125 @@ export class KVBrokerDatabase implements ProvingBrokerDatabase {
51
52
  const result: ProvingJobSettledResult = { status: 'fulfilled', value };
52
53
  await this.jobResults.set(id, jsonStringify(result));
53
54
  }
55
+
56
+ delete() {
57
+ return this.store.delete();
58
+ }
59
+
60
+ close() {
61
+ return this.store.close();
62
+ }
63
+ }
64
+
65
+ export class KVBrokerDatabase implements ProvingBrokerDatabase {
66
+ private metrics: LmdbMetrics;
67
+
68
+ private constructor(
69
+ private epochs: Map<number, SingleEpochDatabase>,
70
+ private config: ProverBrokerConfig,
71
+ client: TelemetryClient,
72
+ private logger: Logger,
73
+ ) {
74
+ this.metrics = new LmdbMetrics(
75
+ client.getMeter('KVBrokerDatabase'),
76
+ {
77
+ [Attributes.DB_DATA_TYPE]: 'prover-broker',
78
+ },
79
+ () => this.estimateSize(),
80
+ );
81
+ }
82
+
83
+ private estimateSize() {
84
+ const sizes = Array.from(this.epochs.values()).map(x => x.estimateSize());
85
+ return {
86
+ mappingSize: this.config.dataStoreMapSizeKB,
87
+ numItems: sizes.reduce((prev, curr) => prev + curr.numItems, 0),
88
+ actualSize: sizes.reduce((prev, curr) => prev + curr.actualSize, 0),
89
+ };
90
+ }
91
+
92
+ public static async new(
93
+ config: ProverBrokerConfig,
94
+ client: TelemetryClient,
95
+ logger = createLogger('prover-client:proving-broker-database'),
96
+ ) {
97
+ const epochs: Map<number, SingleEpochDatabase> = new Map<number, SingleEpochDatabase>();
98
+ const files = await readdir(config.dataDirectory!, { recursive: false, withFileTypes: true });
99
+ for (const file of files) {
100
+ if (!file.isDirectory()) {
101
+ continue;
102
+ }
103
+ const fullDirectory = join(config.dataDirectory!, file.name);
104
+ const epochDirectory = file.name;
105
+ const epochNumber = parseInt(epochDirectory, 10);
106
+ if (!Number.isSafeInteger(epochNumber) || epochNumber < 0) {
107
+ logger.warn(`Found invalid epoch directory ${fullDirectory} when loading epoch databases, ignoring`);
108
+ continue;
109
+ }
110
+ logger.info(
111
+ `Loading broker database for epoch ${epochNumber} from ${fullDirectory} with map size ${config.dataStoreMapSizeKB}KB`,
112
+ );
113
+ const db = AztecLmdbStore.open(fullDirectory, config.dataStoreMapSizeKB, false);
114
+ const epochDb = new SingleEpochDatabase(db);
115
+ epochs.set(epochNumber, epochDb);
116
+ }
117
+ return new KVBrokerDatabase(epochs, config, client, logger);
118
+ }
119
+
120
+ async close(): Promise<void> {
121
+ for (const [_, v] of this.epochs) {
122
+ await v.close();
123
+ }
124
+ }
125
+
126
+ async deleteAllProvingJobsOlderThanEpoch(epochNumber: number): Promise<void> {
127
+ const oldEpochs = Array.from(this.epochs.keys()).filter(e => e < epochNumber);
128
+ for (const old of oldEpochs) {
129
+ const db = this.epochs.get(old);
130
+ if (!db) {
131
+ continue;
132
+ }
133
+ this.logger.info(`Deleting broker database for epoch ${old}`);
134
+ await db.delete();
135
+ this.epochs.delete(old);
136
+ }
137
+ }
138
+
139
+ async addProvingJob(job: ProvingJob): Promise<void> {
140
+ let epochDb = this.epochs.get(job.epochNumber);
141
+ if (!epochDb) {
142
+ const newEpochDirectory = join(this.config.dataDirectory!, job.epochNumber.toString());
143
+ await mkdir(newEpochDirectory, { recursive: true });
144
+ this.logger.info(
145
+ `Creating broker database for epoch ${job.epochNumber} at ${newEpochDirectory} with map size ${this.config.dataStoreMapSizeKB}`,
146
+ );
147
+ const db = AztecLmdbStore.open(newEpochDirectory, this.config.dataStoreMapSizeKB, false);
148
+ epochDb = new SingleEpochDatabase(db);
149
+ this.epochs.set(job.epochNumber, epochDb);
150
+ }
151
+ await epochDb.addProvingJob(job);
152
+ }
153
+
154
+ *allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]> {
155
+ const iterators = Array.from(this.epochs.values()).map(x => x.allProvingJobs());
156
+ for (const it of iterators) {
157
+ yield* it;
158
+ }
159
+ }
160
+
161
+ async setProvingJobError(id: ProvingJobId, reason: string): Promise<void> {
162
+ const epochDb = this.epochs.get(getEpochFromProvingJobId(id));
163
+ if (!epochDb) {
164
+ return;
165
+ }
166
+ await epochDb.setProvingJobError(id, reason);
167
+ }
168
+
169
+ async setProvingJobResult(id: ProvingJobId, value: ProofUri): Promise<void> {
170
+ const epochDb = this.epochs.get(getEpochFromProvingJobId(id));
171
+ if (!epochDb) {
172
+ return;
173
+ }
174
+ await epochDb.setProvingJobResult(id, value);
175
+ }
54
176
  }
@@ -11,10 +11,10 @@ export interface ProvingBrokerDatabase {
11
11
  addProvingJob(request: ProvingJob): Promise<void>;
12
12
 
13
13
  /**
14
- * Removes a proof request from the backend
15
- * @param id - The ID of the proof request to remove
14
+ * Deletes all proving jobs belonging to epochs older than the given epoch
15
+ * @param epochNumber - The epoch number beyond which jobs should be deleted
16
16
  */
17
- deleteProvingJobAndResult(id: ProvingJobId): Promise<void>;
17
+ deleteAllProvingJobsOlderThanEpoch(epochNumber: number): Promise<void>;
18
18
 
19
19
  /**
20
20
  * Returns an iterator over all saved proving jobs
@@ -36,4 +36,9 @@ export interface ProvingBrokerDatabase {
36
36
  * @param err - The error that occurred while processing the proof request
37
37
  */
38
38
  setProvingJobError(id: ProvingJobId, err: string): Promise<void>;
39
+
40
+ /**
41
+ * Closes the database
42
+ */
43
+ close(): Promise<void>;
39
44
  }