@aztec/prover-client 0.69.1 → 0.71.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 (130) hide show
  1. package/dest/bin/get-proof-inputs.d.ts +2 -0
  2. package/dest/bin/get-proof-inputs.d.ts.map +1 -0
  3. package/dest/bin/get-proof-inputs.js +50 -0
  4. package/dest/block_builder/light.d.ts +3 -5
  5. package/dest/block_builder/light.d.ts.map +1 -1
  6. package/dest/block_builder/light.js +9 -22
  7. package/dest/config.d.ts +2 -1
  8. package/dest/config.d.ts.map +1 -1
  9. package/dest/config.js +3 -2
  10. package/dest/mocks/fixtures.d.ts +1 -1
  11. package/dest/mocks/fixtures.d.ts.map +1 -1
  12. package/dest/mocks/fixtures.js +2 -2
  13. package/dest/mocks/test_context.d.ts +1 -1
  14. package/dest/mocks/test_context.d.ts.map +1 -1
  15. package/dest/mocks/test_context.js +11 -12
  16. package/dest/orchestrator/block-building-helpers.d.ts +15 -29
  17. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  18. package/dest/orchestrator/block-building-helpers.js +51 -58
  19. package/dest/orchestrator/block-proving-state.d.ts +40 -44
  20. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  21. package/dest/orchestrator/block-proving-state.js +149 -85
  22. package/dest/orchestrator/epoch-proving-state.d.ts +23 -30
  23. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  24. package/dest/orchestrator/epoch-proving-state.js +92 -65
  25. package/dest/orchestrator/orchestrator.d.ts +17 -48
  26. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  27. package/dest/orchestrator/orchestrator.js +208 -351
  28. package/dest/orchestrator/tx-proving-state.d.ts +10 -6
  29. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  30. package/dest/orchestrator/tx-proving-state.js +57 -46
  31. package/dest/prover-agent/memory-proving-queue.d.ts +4 -4
  32. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  33. package/dest/prover-agent/memory-proving-queue.js +5 -5
  34. package/dest/prover-agent/prover-agent.d.ts +0 -2
  35. package/dest/prover-agent/prover-agent.d.ts.map +1 -1
  36. package/dest/prover-agent/prover-agent.js +7 -9
  37. package/dest/prover-client/factory.d.ts.map +1 -1
  38. package/dest/prover-client/factory.js +3 -3
  39. package/dest/prover-client/prover-client.d.ts +4 -2
  40. package/dest/prover-client/prover-client.d.ts.map +1 -1
  41. package/dest/prover-client/prover-client.js +16 -15
  42. package/dest/prover-client/server-epoch-prover.d.ts +25 -0
  43. package/dest/prover-client/server-epoch-prover.d.ts.map +1 -0
  44. package/dest/prover-client/server-epoch-prover.js +40 -0
  45. package/dest/proving_broker/broker_prover_facade.d.ts +19 -7
  46. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
  47. package/dest/proving_broker/broker_prover_facade.js +271 -49
  48. package/dest/proving_broker/config.d.ts +61 -0
  49. package/dest/proving_broker/config.d.ts.map +1 -0
  50. package/dest/proving_broker/config.js +83 -0
  51. package/dest/proving_broker/factory.d.ts +1 -1
  52. package/dest/proving_broker/factory.d.ts.map +1 -1
  53. package/dest/proving_broker/factory.js +4 -7
  54. package/dest/proving_broker/fixtures.d.ts +5 -0
  55. package/dest/proving_broker/fixtures.d.ts.map +1 -0
  56. package/dest/proving_broker/fixtures.js +12 -0
  57. package/dest/proving_broker/index.d.ts +2 -1
  58. package/dest/proving_broker/index.d.ts.map +1 -1
  59. package/dest/proving_broker/index.js +3 -2
  60. package/dest/proving_broker/proof_store/factory.d.ts +6 -0
  61. package/dest/proving_broker/proof_store/factory.d.ts.map +1 -0
  62. package/dest/proving_broker/proof_store/factory.js +39 -0
  63. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts +13 -0
  64. package/dest/proving_broker/proof_store/gcs_proof_store.d.ts.map +1 -0
  65. package/dest/proving_broker/proof_store/gcs_proof_store.js +46 -0
  66. package/dest/proving_broker/proof_store/index.d.ts +4 -0
  67. package/dest/proving_broker/proof_store/index.d.ts.map +1 -0
  68. package/dest/proving_broker/proof_store/index.js +4 -0
  69. package/dest/proving_broker/proof_store/inline_proof_store.d.ts +14 -0
  70. package/dest/proving_broker/proof_store/inline_proof_store.d.ts.map +1 -0
  71. package/dest/proving_broker/proof_store/inline_proof_store.js +37 -0
  72. package/dest/proving_broker/{proof_store.d.ts → proof_store/proof_store.d.ts} +1 -12
  73. package/dest/proving_broker/proof_store/proof_store.d.ts.map +1 -0
  74. package/dest/proving_broker/proof_store/proof_store.js +2 -0
  75. package/dest/proving_broker/proving_agent.d.ts +4 -4
  76. package/dest/proving_broker/proving_agent.d.ts.map +1 -1
  77. package/dest/proving_broker/proving_agent.js +5 -5
  78. package/dest/proving_broker/proving_broker.d.ts +16 -12
  79. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  80. package/dest/proving_broker/proving_broker.js +307 -274
  81. package/dest/proving_broker/proving_broker_database/memory.d.ts +4 -2
  82. package/dest/proving_broker/proving_broker_database/memory.d.ts.map +1 -1
  83. package/dest/proving_broker/proving_broker_database/memory.js +17 -4
  84. package/dest/proving_broker/proving_broker_database/persisted.d.ts +10 -6
  85. package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
  86. package/dest/proving_broker/proving_broker_database/persisted.js +106 -14
  87. package/dest/proving_broker/proving_broker_database.d.ts +7 -3
  88. package/dest/proving_broker/proving_broker_database.d.ts.map +1 -1
  89. package/dest/proving_broker/proving_job_controller.js +4 -4
  90. package/dest/proving_broker/rpc.d.ts.map +1 -1
  91. package/dest/proving_broker/rpc.js +4 -4
  92. package/dest/test/mock_prover.d.ts +8 -8
  93. package/dest/test/mock_prover.d.ts.map +1 -1
  94. package/dest/test/mock_prover.js +9 -10
  95. package/package.json +14 -12
  96. package/src/bin/get-proof-inputs.ts +60 -0
  97. package/src/block_builder/light.ts +7 -31
  98. package/src/config.ts +4 -4
  99. package/src/mocks/fixtures.ts +1 -1
  100. package/src/mocks/test_context.ts +9 -11
  101. package/src/orchestrator/block-building-helpers.ts +360 -402
  102. package/src/orchestrator/block-proving-state.ts +251 -121
  103. package/src/orchestrator/epoch-proving-state.ts +159 -88
  104. package/src/orchestrator/orchestrator.ts +262 -542
  105. package/src/orchestrator/tx-proving-state.ts +30 -18
  106. package/src/prover-agent/memory-proving-queue.ts +12 -16
  107. package/src/prover-agent/prover-agent.ts +14 -8
  108. package/src/prover-client/factory.ts +2 -3
  109. package/src/prover-client/prover-client.ts +17 -20
  110. package/src/prover-client/server-epoch-prover.ts +44 -0
  111. package/src/proving_broker/broker_prover_facade.ts +347 -67
  112. package/src/proving_broker/config.ts +93 -0
  113. package/src/proving_broker/factory.ts +11 -10
  114. package/src/proving_broker/fixtures.ts +14 -0
  115. package/src/proving_broker/index.ts +2 -1
  116. package/src/proving_broker/proof_store/factory.ts +42 -0
  117. package/src/proving_broker/proof_store/gcs_proof_store.ts +72 -0
  118. package/src/proving_broker/proof_store/index.ts +3 -0
  119. package/src/proving_broker/{proof_store.ts → proof_store/inline_proof_store.ts} +1 -44
  120. package/src/proving_broker/proof_store/proof_store.ts +54 -0
  121. package/src/proving_broker/proving_agent.ts +11 -5
  122. package/src/proving_broker/proving_broker.ts +122 -73
  123. package/src/proving_broker/proving_broker_database/memory.ts +24 -4
  124. package/src/proving_broker/proving_broker_database/persisted.ts +142 -20
  125. package/src/proving_broker/proving_broker_database.ts +8 -3
  126. package/src/proving_broker/proving_job_controller.ts +5 -5
  127. package/src/proving_broker/rpc.ts +2 -3
  128. package/src/test/mock_prover.ts +12 -18
  129. package/dest/proving_broker/proof_store.d.ts.map +0 -1
  130. package/dest/proving_broker/proof_store.js +0 -37
@@ -1,12 +1,15 @@
1
1
  import {
2
2
  type ProofAndVerificationKey,
3
+ type ProofUri,
3
4
  type ProvingJobId,
4
5
  type ProvingJobInputsMap,
5
6
  type ProvingJobProducer,
6
7
  type ProvingJobResultsMap,
8
+ type ProvingJobStatus,
7
9
  ProvingRequestType,
8
10
  type PublicInputsAndRecursiveProof,
9
11
  type ServerCircuitProver,
12
+ makeProvingJobId,
10
13
  } from '@aztec/circuit-types';
11
14
  import {
12
15
  type AVM_PROOF_LENGTH_IN_FIELDS,
@@ -15,8 +18,6 @@ import {
15
18
  type NESTED_RECURSIVE_PROOF_LENGTH,
16
19
  type NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
17
20
  type ParityPublicInputs,
18
- type PrivateKernelEmptyInputData,
19
- type PrivateToRollupKernelCircuitPublicInputs,
20
21
  type RECURSIVE_PROOF_LENGTH,
21
22
  type RootParityInputs,
22
23
  type TUBE_PROOF_LENGTH,
@@ -32,98 +33,380 @@ import {
32
33
  type PublicBaseRollupInputs,
33
34
  type RootRollupInputs,
34
35
  type RootRollupPublicInputs,
36
+ type SingleTxBlockRootRollupInputs,
35
37
  type TubeInputs,
36
38
  } from '@aztec/circuits.js/rollup';
37
39
  import { sha256 } from '@aztec/foundation/crypto';
38
40
  import { createLogger } from '@aztec/foundation/log';
39
- import { retryUntil } from '@aztec/foundation/retry';
41
+ import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
42
+ import { SerialQueue } from '@aztec/foundation/queue';
40
43
  import { truncate } from '@aztec/foundation/string';
41
44
 
42
- import { InlineProofStore, type ProofStore } from './proof_store.js';
45
+ import { InlineProofStore, type ProofStore } from './proof_store/index.js';
43
46
 
44
- // 20 minutes, roughly the length of an Aztec epoch. If a proof isn't ready in this amount of time then we've failed to prove the whole epoch
45
- const MAX_WAIT_MS = 1_200_000;
47
+ // Perform a snapshot sync every 30 seconds
48
+ const SNAPSHOT_SYNC_INTERVAL_MS = 30_000;
49
+
50
+ const MAX_CONCURRENT_JOB_SETTLED_REQUESTS = 10;
51
+ const SNAPSHOT_SYNC_CHECK_MAX_REQUEST_SIZE = 1000;
52
+
53
+ type ProvingJob = {
54
+ id: ProvingJobId;
55
+ type: ProvingRequestType;
56
+ inputsUri: ProofUri;
57
+ promise: PromiseWithResolvers<any>;
58
+ abortFn?: () => Promise<void>;
59
+ signal?: AbortSignal;
60
+ };
46
61
 
47
62
  export class BrokerCircuitProverFacade implements ServerCircuitProver {
63
+ private jobs: Map<ProvingJobId, ProvingJob> = new Map();
64
+ private runningPromise?: RunningPromise;
65
+ private timeOfLastSnapshotSync = Date.now();
66
+ private queue: SerialQueue = new SerialQueue();
67
+ private jobsToRetrieve: Set<ProvingJobId> = new Set();
68
+
48
69
  constructor(
49
70
  private broker: ProvingJobProducer,
50
71
  private proofStore: ProofStore = new InlineProofStore(),
51
- private waitTimeoutMs = MAX_WAIT_MS,
72
+ private failedProofStore?: ProofStore,
52
73
  private pollIntervalMs = 1000,
53
74
  private log = createLogger('prover-client:broker-circuit-prover-facade'),
54
75
  ) {}
55
76
 
56
- private async enqueueAndWaitForJob<T extends ProvingRequestType>(
77
+ private enqueueJob<T extends ProvingRequestType>(
57
78
  id: ProvingJobId,
58
79
  type: T,
59
80
  inputs: ProvingJobInputsMap[T],
60
81
  epochNumber = 0,
61
82
  signal?: AbortSignal,
62
83
  ): Promise<ProvingJobResultsMap[T]> {
84
+ if (!this.queue) {
85
+ throw new Error('BrokerCircuitProverFacade not started');
86
+ }
87
+ return this.queue!.put(() => this._enqueueJob(id, type, inputs, epochNumber, signal)).then(
88
+ ({ enqueuedPromise }) => enqueuedPromise,
89
+ );
90
+ }
91
+
92
+ private async _enqueueJob<T extends ProvingRequestType>(
93
+ id: ProvingJobId,
94
+ type: T,
95
+ inputs: ProvingJobInputsMap[T],
96
+ epochNumber = 0,
97
+ signal?: AbortSignal,
98
+ ): Promise<{ enqueuedPromise: Promise<ProvingJobResultsMap[T]> }> {
99
+ // Check if there is already a promise for this job
100
+ const existingPromise = this.jobs.get(id);
101
+ if (existingPromise) {
102
+ this.log.verbose(`Job already found in facade id=${id} type=${ProvingRequestType[type]}`, {
103
+ provingJobId: id,
104
+ provingJobType: ProvingRequestType[type],
105
+ epochNumber,
106
+ });
107
+ return { enqueuedPromise: existingPromise.promise.promise as Promise<ProvingJobResultsMap[T]> };
108
+ }
63
109
  const inputsUri = await this.proofStore.saveProofInput(id, type, inputs);
64
- await this.broker.enqueueProvingJob({
110
+ const jobStatus = await this.broker.enqueueProvingJob({
65
111
  id,
66
112
  type,
67
113
  inputsUri,
68
114
  epochNumber,
69
115
  });
70
116
 
71
- this.log.verbose(
72
- `Sent proving job to broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`,
73
- {
74
- provingJobId: id,
75
- provingJobType: ProvingRequestType[type],
76
- epochNumber,
77
- inputsUri: truncate(inputsUri),
78
- },
79
- );
80
-
81
- // notify broker of cancelled job
117
+ // Create a promise for this job id, regardless of whether it was enqueued at the broker
118
+ // The running promise will monitor for the job to be completed and resolve it either way
119
+ const promise = promiseWithResolvers<ProvingJobResultsMap[T]>();
82
120
  const abortFn = async () => {
83
121
  signal?.removeEventListener('abort', abortFn);
84
122
  await this.broker.cancelProvingJob(id);
85
123
  };
124
+ const job: ProvingJob = {
125
+ id,
126
+ type,
127
+ inputsUri,
128
+ promise,
129
+ abortFn,
130
+ signal,
131
+ };
132
+ this.jobs.set(id, job);
86
133
 
87
- signal?.addEventListener('abort', abortFn);
134
+ // If we are here then the job was successfully accepted by the broker
135
+ // the returned status is for before any action was performed
136
+ if (jobStatus.status === 'not-found') {
137
+ // Job added for the first time
138
+ // notify the broker if job is aborted
139
+ signal?.addEventListener('abort', abortFn);
88
140
 
89
- try {
90
- // loop here until the job settles
91
- // NOTE: this could also terminate because the job was cancelled through event listener above
92
- const result = await retryUntil(
93
- async () => {
94
- try {
95
- return await this.broker.waitForJobToSettle(id);
96
- } catch (err) {
97
- // waitForJobToSettle can only fail for network errors
98
- // keep retrying until we time out
99
- }
141
+ this.log.verbose(
142
+ `Job enqueued with broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`,
143
+ {
144
+ provingJobId: id,
145
+ provingJobType: ProvingRequestType[type],
146
+ epochNumber,
147
+ inputsUri: truncate(inputsUri),
148
+ numOutstandingJobs: this.jobs.size,
149
+ },
150
+ );
151
+ } else if (jobStatus.status === 'fulfilled' || jobStatus.status === 'rejected') {
152
+ // Job was already completed by the broker
153
+ // No need to notify the broker on aborted job
154
+ job.abortFn = undefined;
155
+ this.log.verbose(
156
+ `Job already completed when sent to broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`,
157
+ {
158
+ provingJobId: id,
159
+ provingJobType: ProvingRequestType[type],
160
+ epochNumber,
161
+ inputsUri: truncate(inputsUri),
100
162
  },
101
- `Proving job=${id} type=${ProvingRequestType[type]}`,
102
- this.waitTimeoutMs / 1000,
103
- this.pollIntervalMs / 1000,
104
163
  );
105
164
 
165
+ // Job was not enqueued. It must be completed already, add to our set of already completed jobs
166
+ this.jobsToRetrieve.add(id);
167
+ } else {
168
+ // Job was previously sent to the broker but is not completed
169
+ // notify the broker if job is aborted
170
+ signal?.addEventListener('abort', abortFn);
171
+ this.log.verbose(
172
+ `Job already in queue or in progress when sent to broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`,
173
+ {
174
+ provingJobId: id,
175
+ provingJobType: ProvingRequestType[type],
176
+ epochNumber,
177
+ inputsUri: truncate(inputsUri),
178
+ },
179
+ );
180
+ }
181
+ const typedPromise = promise.promise as Promise<ProvingJobResultsMap[T]>;
182
+ return { enqueuedPromise: typedPromise };
183
+ }
184
+
185
+ public start() {
186
+ if (this.runningPromise) {
187
+ throw new Error('BrokerCircuitProverFacade already started');
188
+ }
189
+
190
+ this.log.verbose('Starting BrokerCircuitProverFacade');
191
+
192
+ this.runningPromise = new RunningPromise(() => this.monitorForCompletedJobs(), this.log, this.pollIntervalMs);
193
+ this.runningPromise.start();
194
+
195
+ this.queue = new SerialQueue();
196
+ this.queue.start();
197
+ }
198
+
199
+ public async stop(): Promise<void> {
200
+ if (!this.runningPromise) {
201
+ throw new Error('BrokerCircuitProverFacade not started');
202
+ }
203
+ this.log.verbose('Stopping BrokerCircuitProverFacade');
204
+ await this.runningPromise.stop();
205
+
206
+ if (this.queue) {
207
+ await this.queue.cancel();
208
+ await this.queue.end();
209
+ }
210
+
211
+ // Reject any outstanding promises as stopped
212
+ for (const [_, v] of this.jobs) {
213
+ v.promise.reject(new Error('Broker facade stopped'));
214
+ }
215
+ this.jobs.clear();
216
+ }
217
+
218
+ private async updateCompletedJobs() {
219
+ // Here we check for completed jobs. If everything works well (there are no service restarts etc) then all we need to do
220
+ // to maintain correct job state is to check for incrementally completed jobs. i.e. call getCompletedJobs with an empty array
221
+ // However, if there are any problems then we may lose sync with the broker's actual set of completed jobs.
222
+ // In this case we need to perform a full snapshot sync. This involves sending all of our outstanding job Ids to the broker
223
+ // and have the broker report on whether they are completed or not.
224
+ // We perform an incremental sync on every call of this function with a full snapshot sync periodically.
225
+ // This should keep us in sync without over-burdening the broker with snapshot sync requests
226
+
227
+ const getAllCompletedJobs = async (ids: ProvingJobId[]) => {
228
+ // In this function we take whatever set of snapshot ids and we ask the broker for completed job notifications
229
+ // We collect all returned notifications and return them
230
+ const allCompleted = new Set<ProvingJobId>();
231
+ try {
232
+ let numRequests = 0;
233
+ while (ids.length > 0) {
234
+ const slice = ids.splice(0, SNAPSHOT_SYNC_CHECK_MAX_REQUEST_SIZE);
235
+ const completed = await this.broker.getCompletedJobs(slice);
236
+ completed.forEach(id => allCompleted.add(id));
237
+ ++numRequests;
238
+ }
239
+ if (numRequests === 0) {
240
+ const final = await this.broker.getCompletedJobs([]);
241
+ final.forEach(id => allCompleted.add(id));
242
+ }
243
+ } catch (err) {
244
+ this.log.error(`Error thrown when requesting completed job notifications from the broker`, err);
245
+ }
246
+ return allCompleted;
247
+ };
248
+
249
+ const snapshotSyncIds = [];
250
+ const currentTime = Date.now();
251
+ const secondsSinceLastSnapshotSync = currentTime - this.timeOfLastSnapshotSync;
252
+ if (secondsSinceLastSnapshotSync > SNAPSHOT_SYNC_INTERVAL_MS) {
253
+ this.timeOfLastSnapshotSync = currentTime;
254
+ snapshotSyncIds.push(...this.jobs.keys());
255
+ this.log.trace(`Performing full snapshot sync of completed jobs with ${snapshotSyncIds.length} job(s)`);
256
+ } else {
257
+ this.log.trace(`Performing incremental sync of completed jobs`);
258
+ }
259
+
260
+ // Now request the notifications from the broker
261
+ const snapshotIdsLength = snapshotSyncIds.length;
262
+ const completedJobs = await getAllCompletedJobs(snapshotSyncIds);
263
+
264
+ // We now have an additional set of completed job notifications to add to our cached set giving us the full set of jobs that we have been told are ready
265
+ // We filter this list to what we actually need, in case for any reason it is different and store in our cache
266
+ const allJobsReady = [...completedJobs, ...this.jobsToRetrieve];
267
+ this.jobsToRetrieve = new Set(allJobsReady.filter(id => this.jobs.has(id)));
268
+
269
+ if (completedJobs.size > 0) {
270
+ this.log.verbose(
271
+ `Check for job completion notifications returned ${completedJobs.size} job(s), snapshot ids length: ${snapshotIdsLength}, num outstanding jobs: ${this.jobs.size}, total jobs ready: ${this.jobsToRetrieve.size}`,
272
+ );
273
+ } else {
274
+ this.log.trace(
275
+ `Check for job completion notifications returned 0 jobs, snapshot ids length: ${snapshotIdsLength}, num outstanding jobs: ${this.jobs.size}, total jobs ready: ${this.jobsToRetrieve.size}`,
276
+ );
277
+ }
278
+ }
279
+
280
+ private async retrieveJobsThatShouldBeReady() {
281
+ const convertJobResult = async <T extends ProvingRequestType>(
282
+ result: ProvingJobStatus,
283
+ jobType: ProvingRequestType,
284
+ ): Promise<{
285
+ success: boolean;
286
+ reason?: string;
287
+ result?: ProvingJobResultsMap[T];
288
+ }> => {
106
289
  if (result.status === 'fulfilled') {
107
290
  const output = await this.proofStore.getProofOutput(result.value);
108
- if (output.type === type) {
109
- return output.result as ProvingJobResultsMap[T];
291
+ if (output.type === jobType) {
292
+ return { result: output.result as ProvingJobResultsMap[T], success: true };
110
293
  } else {
111
- throw new Error(`Unexpected proof type: ${output.type}. Expected: ${type}`);
294
+ return { success: false, reason: `Unexpected proof type: ${output.type}. Expected: ${jobType}` };
112
295
  }
296
+ } else if (result.status === 'rejected') {
297
+ return { success: false, reason: result.reason };
113
298
  } else {
114
- throw new Error(result.reason);
299
+ throw new Error(`Unexpected proving job status ${result.status}`);
115
300
  }
116
- } finally {
117
- signal?.removeEventListener('abort', abortFn);
301
+ };
302
+
303
+ const processJob = async (job: ProvingJob) => {
304
+ // First retrieve the settled job from the broker
305
+ this.log.debug(`Received notification of completed job id=${job.id} type=${ProvingRequestType[job.type]}`);
306
+ let settledResult;
307
+ try {
308
+ settledResult = await this.broker.getProvingJobStatus(job.id);
309
+ } catch (err) {
310
+ // If an error occurs retrieving the job result then just log it and move on.
311
+ // We will try again on the next iteration
312
+ this.log.error(
313
+ `Error retrieving job result from broker job id=${job.id} type=${ProvingRequestType[job.type]}`,
314
+ err,
315
+ );
316
+ return false;
317
+ }
318
+
319
+ // Then convert the result and resolve/reject the promise
320
+ let result;
321
+ try {
322
+ result = await convertJobResult(settledResult, job.type);
323
+ } catch (err) {
324
+ // If an error occurs retrieving the job result then just log it and move on.
325
+ // We will try again on the next iteration
326
+ this.log.error(`Error processing job result job id=${job.id} type=${ProvingRequestType[job.type]}`, err);
327
+ return false;
328
+ }
329
+
330
+ if (result.success) {
331
+ this.log.verbose(`Resolved proving job id=${job.id} type=${ProvingRequestType[job.type]}`);
332
+ job.promise.resolve(result.result);
333
+ } else {
334
+ this.log.error(
335
+ `Resolving proving job with error id=${job.id} type=${ProvingRequestType[job.type]}`,
336
+ result.reason,
337
+ );
338
+ if (result.reason !== 'Aborted') {
339
+ void this.backupFailedProofInputs(job);
340
+ }
341
+ job.promise.reject(new Error(result.reason));
342
+ }
343
+
344
+ if (job.abortFn && job.signal) {
345
+ job.signal?.removeEventListener('abort', job.abortFn);
346
+ }
347
+
348
+ // Job is now processed removed from our cache
349
+ this.jobs.delete(job.id);
350
+ this.jobsToRetrieve.delete(job.id);
351
+ return true;
352
+ };
353
+
354
+ const toBeRetrieved = Array.from(this.jobsToRetrieve.values())
355
+ .map(id => this.jobs.get(id)!)
356
+ .filter(x => x !== undefined);
357
+ const totalJobsToRetrieve = toBeRetrieved.length;
358
+ let totalJobsRetrieved = 0;
359
+ while (toBeRetrieved.length > 0) {
360
+ const slice = toBeRetrieved.splice(0, MAX_CONCURRENT_JOB_SETTLED_REQUESTS);
361
+ const results = await Promise.all(slice.map(job => processJob(job!)));
362
+ totalJobsRetrieved += results.filter(x => x).length;
363
+ }
364
+ if (totalJobsToRetrieve > 0) {
365
+ this.log.verbose(
366
+ `Successfully retrieved ${totalJobsRetrieved} of ${totalJobsToRetrieve} jobs that should be ready, total ready jobs is now: ${this.jobsToRetrieve.size}`,
367
+ );
118
368
  }
119
369
  }
120
370
 
371
+ private async backupFailedProofInputs(job: ProvingJob) {
372
+ try {
373
+ if (!this.failedProofStore) {
374
+ return;
375
+ }
376
+ const inputs = await this.proofStore.getProofInput(job.inputsUri);
377
+ const uri = await this.failedProofStore.saveProofInput(job.id, inputs.type, inputs.inputs);
378
+ this.log.info(`Stored proof inputs for failed job id=${job.id} type=${ProvingRequestType[job.type]} at ${uri}`, {
379
+ id: job.id,
380
+ type: job.type,
381
+ uri,
382
+ });
383
+ } catch (err) {
384
+ this.log.error(
385
+ `Error backing up proof inputs for failed job id=${job.id} type=${ProvingRequestType[job.type]}`,
386
+ err,
387
+ );
388
+ }
389
+ }
390
+
391
+ private async monitorForCompletedJobs() {
392
+ // Monitoring for completed jobs involves 2 stages.
393
+
394
+ // 1. Update our list of completed jobs.
395
+ // We poll the broker for any new job completion notifications and after filtering/deduplication add them to our cached
396
+ // list of jobs that we have been told are ready.
397
+ await this.updateCompletedJobs();
398
+
399
+ // 2. Retrieve the jobs that should be ready.
400
+ // We have a list of jobs that we have been told are ready, so we go ahead and ask for their results
401
+ await this.retrieveJobsThatShouldBeReady();
402
+ }
403
+
121
404
  getAvmProof(
122
405
  inputs: AvmCircuitInputs,
123
406
  signal?: AbortSignal,
124
407
  epochNumber?: number,
125
408
  ): Promise<ProofAndVerificationKey<typeof AVM_PROOF_LENGTH_IN_FIELDS>> {
126
- return this.enqueueAndWaitForJob(
409
+ return this.enqueueJob(
127
410
  this.generateId(ProvingRequestType.PUBLIC_VM, inputs, epochNumber),
128
411
  ProvingRequestType.PUBLIC_VM,
129
412
  inputs,
@@ -137,7 +420,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
137
420
  signal?: AbortSignal,
138
421
  epochNumber?: number,
139
422
  ): Promise<PublicInputsAndRecursiveProof<ParityPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
140
- return this.enqueueAndWaitForJob(
423
+ return this.enqueueJob(
141
424
  this.generateId(ProvingRequestType.BASE_PARITY, inputs, epochNumber),
142
425
  ProvingRequestType.BASE_PARITY,
143
426
  inputs,
@@ -153,7 +436,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
153
436
  ): Promise<
154
437
  PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
155
438
  > {
156
- return this.enqueueAndWaitForJob(
439
+ return this.enqueueJob(
157
440
  this.generateId(ProvingRequestType.BLOCK_MERGE_ROLLUP, input, epochNumber),
158
441
  ProvingRequestType.BLOCK_MERGE_ROLLUP,
159
442
  input,
@@ -169,7 +452,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
169
452
  ): Promise<
170
453
  PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
171
454
  > {
172
- return this.enqueueAndWaitForJob(
455
+ return this.enqueueJob(
173
456
  this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input, epochNumber),
174
457
  ProvingRequestType.BLOCK_ROOT_ROLLUP,
175
458
  input,
@@ -178,36 +461,33 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
178
461
  );
179
462
  }
180
463
 
181
- getEmptyBlockRootRollupProof(
182
- input: EmptyBlockRootRollupInputs,
464
+ getSingleTxBlockRootRollupProof(
465
+ input: SingleTxBlockRootRollupInputs,
183
466
  signal?: AbortSignal,
184
467
  epochNumber?: number,
185
468
  ): Promise<
186
469
  PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
187
470
  > {
188
- return this.enqueueAndWaitForJob(
189
- this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input, epochNumber),
190
- ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP,
471
+ return this.enqueueJob(
472
+ this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input, epochNumber),
473
+ ProvingRequestType.SINGLE_TX_BLOCK_ROOT_ROLLUP,
191
474
  input,
192
475
  epochNumber,
193
476
  signal,
194
477
  );
195
478
  }
196
479
 
197
- getEmptyPrivateKernelProof(
198
- inputs: PrivateKernelEmptyInputData,
480
+ getEmptyBlockRootRollupProof(
481
+ input: EmptyBlockRootRollupInputs,
199
482
  signal?: AbortSignal,
200
483
  epochNumber?: number,
201
484
  ): Promise<
202
- PublicInputsAndRecursiveProof<
203
- PrivateToRollupKernelCircuitPublicInputs,
204
- typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
205
- >
485
+ PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
206
486
  > {
207
- return this.enqueueAndWaitForJob(
208
- this.generateId(ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs, epochNumber),
209
- ProvingRequestType.PRIVATE_KERNEL_EMPTY,
210
- inputs,
487
+ return this.enqueueJob(
488
+ this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input, epochNumber),
489
+ ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP,
490
+ input,
211
491
  epochNumber,
212
492
  signal,
213
493
  );
@@ -220,7 +500,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
220
500
  ): Promise<
221
501
  PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
222
502
  > {
223
- return this.enqueueAndWaitForJob(
503
+ return this.enqueueJob(
224
504
  this.generateId(ProvingRequestType.MERGE_ROLLUP, input, epochNumber),
225
505
  ProvingRequestType.MERGE_ROLLUP,
226
506
  input,
@@ -235,7 +515,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
235
515
  ): Promise<
236
516
  PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
237
517
  > {
238
- return this.enqueueAndWaitForJob(
518
+ return this.enqueueJob(
239
519
  this.generateId(ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput, epochNumber),
240
520
  ProvingRequestType.PRIVATE_BASE_ROLLUP,
241
521
  baseRollupInput,
@@ -251,7 +531,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
251
531
  ): Promise<
252
532
  PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH>
253
533
  > {
254
- return this.enqueueAndWaitForJob(
534
+ return this.enqueueJob(
255
535
  this.generateId(ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs, epochNumber),
256
536
  ProvingRequestType.PUBLIC_BASE_ROLLUP,
257
537
  inputs,
@@ -265,7 +545,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
265
545
  signal?: AbortSignal,
266
546
  epochNumber?: number,
267
547
  ): Promise<PublicInputsAndRecursiveProof<ParityPublicInputs, typeof NESTED_RECURSIVE_PROOF_LENGTH>> {
268
- return this.enqueueAndWaitForJob(
548
+ return this.enqueueJob(
269
549
  this.generateId(ProvingRequestType.ROOT_PARITY, inputs, epochNumber),
270
550
  ProvingRequestType.ROOT_PARITY,
271
551
  inputs,
@@ -279,7 +559,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
279
559
  signal?: AbortSignal,
280
560
  epochNumber?: number,
281
561
  ): Promise<PublicInputsAndRecursiveProof<RootRollupPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
282
- return this.enqueueAndWaitForJob(
562
+ return this.enqueueJob(
283
563
  this.generateId(ProvingRequestType.ROOT_ROLLUP, input, epochNumber),
284
564
  ProvingRequestType.ROOT_ROLLUP,
285
565
  input,
@@ -293,7 +573,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
293
573
  signal?: AbortSignal,
294
574
  epochNumber?: number,
295
575
  ): Promise<ProofAndVerificationKey<typeof TUBE_PROOF_LENGTH>> {
296
- return this.enqueueAndWaitForJob(
576
+ return this.enqueueJob(
297
577
  this.generateId(ProvingRequestType.TUBE_PROOF, tubeInput, epochNumber),
298
578
  ProvingRequestType.TUBE_PROOF,
299
579
  tubeInput,
@@ -304,6 +584,6 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
304
584
 
305
585
  private generateId(type: ProvingRequestType, inputs: { toBuffer(): Buffer }, epochNumber = 0) {
306
586
  const inputsHash = sha256(inputs.toBuffer());
307
- return `${epochNumber}:${ProvingRequestType[type]}:${inputsHash.toString('hex')}`;
587
+ return makeProvingJobId(epochNumber, type, inputsHash.toString('hex'));
308
588
  }
309
589
  }
@@ -0,0 +1,93 @@
1
+ import { ProvingRequestType } from '@aztec/circuit-types';
2
+ import { type ConfigMappingsType, booleanConfigHelper, numberConfigHelper } from '@aztec/foundation/config';
3
+ import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
4
+
5
+ import { z } from 'zod';
6
+
7
+ export const ProverBrokerConfig = z.object({
8
+ /** If starting a prover broker locally, the max number of retries per proving job */
9
+ proverBrokerJobMaxRetries: z.number(),
10
+ /** If starting a prover broker locally, the time after which a job times out and gets assigned to a different agent */
11
+ proverBrokerJobTimeoutMs: z.number(),
12
+ /** If starting a prover broker locally, the interval the broker checks for timed out jobs */
13
+ proverBrokerPollIntervalMs: z.number(),
14
+ /** If starting a prover broker locally, the directory to store broker data */
15
+ dataDirectory: z.string().optional(),
16
+ /** The size of the data store map */
17
+ dataStoreMapSizeKB: z.number(),
18
+ });
19
+
20
+ export type ProverBrokerConfig = z.infer<typeof ProverBrokerConfig> &
21
+ Pick<DataStoreConfig, 'dataStoreMapSizeKB' | 'dataDirectory'>;
22
+
23
+ export const proverBrokerConfigMappings: ConfigMappingsType<ProverBrokerConfig> = {
24
+ proverBrokerJobTimeoutMs: {
25
+ env: 'PROVER_BROKER_JOB_TIMEOUT_MS',
26
+ description: 'Jobs are retried if not kept alive for this long',
27
+ ...numberConfigHelper(30_000),
28
+ },
29
+ proverBrokerPollIntervalMs: {
30
+ env: 'PROVER_BROKER_POLL_INTERVAL_MS',
31
+ description: 'The interval to check job health status',
32
+ ...numberConfigHelper(1_000),
33
+ },
34
+ proverBrokerJobMaxRetries: {
35
+ env: 'PROVER_BROKER_JOB_MAX_RETRIES',
36
+ description: 'If starting a prover broker locally, the max number of retries per proving job',
37
+ ...numberConfigHelper(3),
38
+ },
39
+ ...dataConfigMappings,
40
+ };
41
+
42
+ export const ProverAgentConfig = z.object({
43
+ /** The number of prover agents to start */
44
+ proverAgentCount: z.number(),
45
+ /** The types of proofs the prover agent can generate */
46
+ proverAgentProofTypes: z.array(z.nativeEnum(ProvingRequestType)),
47
+ /** How often the prover agents poll for jobs */
48
+ proverAgentPollIntervalMs: z.number(),
49
+ /** The URL where this agent takes jobs from */
50
+ proverBrokerUrl: z.string().optional(),
51
+ /** Whether to construct real proofs */
52
+ realProofs: z.boolean(),
53
+ /** Artificial delay to introduce to all operations to the test prover. */
54
+ proverTestDelayMs: z.number(),
55
+ });
56
+
57
+ export type ProverAgentConfig = z.infer<typeof ProverAgentConfig>;
58
+
59
+ export const proverAgentConfigMappings: ConfigMappingsType<ProverAgentConfig> = {
60
+ proverAgentCount: {
61
+ env: 'PROVER_AGENT_COUNT',
62
+ description: 'Whether this prover has a local prover agent',
63
+ ...numberConfigHelper(1),
64
+ },
65
+ proverAgentPollIntervalMs: {
66
+ env: 'PROVER_AGENT_POLL_INTERVAL_MS',
67
+ description: 'The interval agents poll for jobs at',
68
+ ...numberConfigHelper(100),
69
+ },
70
+ proverAgentProofTypes: {
71
+ env: 'PROVER_AGENT_PROOF_TYPES',
72
+ description: 'The types of proofs the prover agent can generate',
73
+ parseEnv: (val: string) =>
74
+ val
75
+ .split(',')
76
+ .map(v => ProvingRequestType[v as any])
77
+ .filter(v => typeof v === 'number'),
78
+ },
79
+ proverBrokerUrl: {
80
+ env: 'PROVER_BROKER_HOST',
81
+ description: 'The URL where this agent takes jobs from',
82
+ },
83
+ realProofs: {
84
+ env: 'PROVER_REAL_PROOFS',
85
+ description: 'Whether to construct real proofs',
86
+ ...booleanConfigHelper(false),
87
+ },
88
+ proverTestDelayMs: {
89
+ env: 'PROVER_TEST_DELAY_MS',
90
+ description: 'Artificial delay to introduce to all operations to the test prover.',
91
+ ...numberConfigHelper(0),
92
+ },
93
+ };