@aztec/prover-client 0.67.1 → 0.68.1

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 (77) hide show
  1. package/dest/block_builder/light.d.ts +4 -3
  2. package/dest/block_builder/light.d.ts.map +1 -1
  3. package/dest/block_builder/light.js +23 -16
  4. package/dest/index.d.ts +0 -1
  5. package/dest/index.d.ts.map +1 -1
  6. package/dest/index.js +1 -2
  7. package/dest/mocks/fixtures.d.ts.map +1 -1
  8. package/dest/mocks/fixtures.js +3 -3
  9. package/dest/mocks/test_context.d.ts +3 -2
  10. package/dest/mocks/test_context.d.ts.map +1 -1
  11. package/dest/mocks/test_context.js +24 -12
  12. package/dest/orchestrator/block-building-helpers.d.ts +8 -2
  13. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  14. package/dest/orchestrator/block-building-helpers.js +20 -7
  15. package/dest/orchestrator/block-proving-state.d.ts +8 -5
  16. package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
  17. package/dest/orchestrator/block-proving-state.js +16 -7
  18. package/dest/orchestrator/epoch-proving-state.d.ts +3 -2
  19. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  20. package/dest/orchestrator/epoch-proving-state.js +3 -3
  21. package/dest/orchestrator/orchestrator.d.ts +10 -7
  22. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  23. package/dest/orchestrator/orchestrator.js +94 -56
  24. package/dest/orchestrator/tx-proving-state.d.ts +2 -1
  25. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  26. package/dest/orchestrator/tx-proving-state.js +3 -2
  27. package/dest/prover-agent/memory-proving-queue.d.ts +4 -2
  28. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  29. package/dest/prover-agent/memory-proving-queue.js +240 -224
  30. package/dest/prover-agent/prover-agent.d.ts +11 -2
  31. package/dest/prover-agent/prover-agent.d.ts.map +1 -1
  32. package/dest/prover-agent/prover-agent.js +186 -159
  33. package/dest/prover-client/prover-client.d.ts +2 -3
  34. package/dest/prover-client/prover-client.d.ts.map +1 -1
  35. package/dest/prover-client/prover-client.js +4 -7
  36. package/dest/proving_broker/{caching_broker_facade.d.ts → broker_prover_facade.d.ts} +6 -9
  37. package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -0
  38. package/dest/proving_broker/broker_prover_facade.js +107 -0
  39. package/dest/proving_broker/proving_agent.d.ts +4 -3
  40. package/dest/proving_broker/proving_agent.d.ts.map +1 -1
  41. package/dest/proving_broker/proving_agent.js +73 -64
  42. package/dest/proving_broker/proving_broker.d.ts +4 -3
  43. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  44. package/dest/proving_broker/proving_broker.js +403 -324
  45. package/dest/proving_broker/proving_job_controller.d.ts +2 -1
  46. package/dest/proving_broker/proving_job_controller.d.ts.map +1 -1
  47. package/dest/proving_broker/proving_job_controller.js +15 -14
  48. package/dest/proving_broker/rpc.d.ts.map +1 -1
  49. package/dest/proving_broker/rpc.js +1 -2
  50. package/dest/test/mock_prover.d.ts +6 -6
  51. package/dest/test/mock_prover.d.ts.map +1 -1
  52. package/dest/test/mock_prover.js +3 -6
  53. package/package.json +16 -15
  54. package/src/block_builder/light.ts +23 -16
  55. package/src/index.ts +0 -1
  56. package/src/mocks/fixtures.ts +2 -2
  57. package/src/mocks/test_context.ts +33 -16
  58. package/src/orchestrator/block-building-helpers.ts +34 -18
  59. package/src/orchestrator/block-proving-state.ts +18 -8
  60. package/src/orchestrator/epoch-proving-state.ts +1 -4
  61. package/src/orchestrator/orchestrator.ts +113 -62
  62. package/src/orchestrator/tx-proving-state.ts +6 -4
  63. package/src/prover-agent/memory-proving-queue.ts +21 -15
  64. package/src/prover-agent/prover-agent.ts +65 -46
  65. package/src/prover-client/prover-client.ts +3 -10
  66. package/src/proving_broker/{caching_broker_facade.ts → broker_prover_facade.ts} +46 -83
  67. package/src/proving_broker/proving_agent.ts +72 -76
  68. package/src/proving_broker/proving_broker.ts +114 -36
  69. package/src/proving_broker/proving_job_controller.ts +13 -12
  70. package/src/proving_broker/rpc.ts +0 -1
  71. package/src/test/mock_prover.ts +17 -14
  72. package/dest/proving_broker/caching_broker_facade.d.ts.map +0 -1
  73. package/dest/proving_broker/caching_broker_facade.js +0 -153
  74. package/dest/proving_broker/prover_cache/memory.d.ts +0 -9
  75. package/dest/proving_broker/prover_cache/memory.d.ts.map +0 -1
  76. package/dest/proving_broker/prover_cache/memory.js +0 -16
  77. package/src/proving_broker/prover_cache/memory.ts +0 -20
@@ -11,26 +11,29 @@ import {
11
11
  import { createLogger } from '@aztec/foundation/log';
12
12
  import { RunningPromise } from '@aztec/foundation/running-promise';
13
13
  import { elapsed } from '@aztec/foundation/timer';
14
+ import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
15
+ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
14
16
 
15
17
  import { InlineProofStore } from '../proving_broker/proof_store.js';
16
18
 
17
19
  const PRINT_THRESHOLD_NS = 6e10; // 60 seconds
18
20
 
21
+ type InFlightPromise = {
22
+ id: string;
23
+ type: ProvingRequestType;
24
+ promise: Promise<any>;
25
+ };
26
+
19
27
  /**
20
28
  * A helper class that encapsulates a circuit prover and connects it to a job source.
21
29
  */
22
- export class ProverAgent implements ProverAgentApi {
23
- private inFlightPromises = new Map<
24
- string,
25
- {
26
- id: string;
27
- type: ProvingRequestType;
28
- promise: Promise<any>;
29
- }
30
- >();
30
+ export class ProverAgent implements ProverAgentApi, Traceable {
31
+ private inFlightPromises = new Map<string, InFlightPromise>();
31
32
  private runningPromise?: RunningPromise;
32
33
  private proofInputsDatabase = new InlineProofStore();
33
34
 
35
+ public readonly tracer: Tracer;
36
+
34
37
  constructor(
35
38
  /** The prover implementation to defer jobs to */
36
39
  private circuitProver: ServerCircuitProver,
@@ -38,8 +41,13 @@ export class ProverAgent implements ProverAgentApi {
38
41
  private maxConcurrency = 1,
39
42
  /** How long to wait between jobs */
40
43
  private pollIntervalMs = 100,
44
+ /** Telemetry client */
45
+ private telemetry: TelemetryClient = new NoopTelemetryClient(),
46
+ /** Logger */
41
47
  private log = createLogger('prover-client:prover-agent'),
42
- ) {}
48
+ ) {
49
+ this.tracer = telemetry.getTracer('ProverAgent');
50
+ }
43
51
 
44
52
  setMaxConcurrency(maxConcurrency: number): Promise<void> {
45
53
  if (maxConcurrency < 1) {
@@ -74,49 +82,53 @@ export class ProverAgent implements ProverAgentApi {
74
82
 
75
83
  let lastPrint = process.hrtime.bigint();
76
84
 
77
- this.runningPromise = new RunningPromise(async () => {
78
- for (const jobId of this.inFlightPromises.keys()) {
79
- await jobSource.heartbeat(jobId);
80
- }
81
-
82
- const now = process.hrtime.bigint();
83
-
84
- if (now - lastPrint >= PRINT_THRESHOLD_NS) {
85
- // only log if we're actually doing work
86
- if (this.inFlightPromises.size > 0) {
87
- const jobs = Array.from(this.inFlightPromises.values())
88
- .map(job => `id=${job.id},type=${ProvingRequestType[job.type]}`)
89
- .join(' ');
90
- this.log.info(`Agent is running with ${this.inFlightPromises.size} in-flight jobs: ${jobs}`);
85
+ this.runningPromise = new RunningPromise(
86
+ async () => {
87
+ for (const jobId of this.inFlightPromises.keys()) {
88
+ await jobSource.heartbeat(jobId);
91
89
  }
92
- lastPrint = now;
93
- }
94
90
 
95
- while (this.inFlightPromises.size < this.maxConcurrency) {
96
- try {
97
- const job = await jobSource.getProvingJob();
98
- if (!job) {
99
- // job source is fully drained, sleep for a bit and try again
100
- return;
91
+ const now = process.hrtime.bigint();
92
+
93
+ if (now - lastPrint >= PRINT_THRESHOLD_NS) {
94
+ // only log if we're actually doing work
95
+ if (this.inFlightPromises.size > 0) {
96
+ const jobs = Array.from(this.inFlightPromises.values())
97
+ .map(job => `id=${job.id},type=${ProvingRequestType[job.type]}`)
98
+ .join(' ');
99
+ this.log.info(`Agent is running with ${this.inFlightPromises.size} in-flight jobs: ${jobs}`);
101
100
  }
101
+ lastPrint = now;
102
+ }
102
103
 
104
+ while (this.inFlightPromises.size < this.maxConcurrency) {
103
105
  try {
104
- const promise = this.work(jobSource, job).finally(() => this.inFlightPromises.delete(job.id));
105
- this.inFlightPromises.set(job.id, {
106
- id: job.id,
107
- type: job.type,
108
- promise,
109
- });
106
+ const job = await jobSource.getProvingJob();
107
+ if (!job) {
108
+ // job source is fully drained, sleep for a bit and try again
109
+ return;
110
+ }
111
+
112
+ try {
113
+ const promise = this.work(jobSource, job).finally(() => this.inFlightPromises.delete(job.id));
114
+ this.inFlightPromises.set(job.id, {
115
+ id: job.id,
116
+ type: job.type,
117
+ promise,
118
+ });
119
+ } catch (err) {
120
+ this.log.warn(
121
+ `Error processing job! type=${ProvingRequestType[job.type]}: ${err}. ${(err as Error).stack}`,
122
+ );
123
+ }
110
124
  } catch (err) {
111
- this.log.warn(
112
- `Error processing job! type=${ProvingRequestType[job.type]}: ${err}. ${(err as Error).stack}`,
113
- );
125
+ this.log.error(`Error fetching job`, err);
114
126
  }
115
- } catch (err) {
116
- this.log.error(`Error fetching job`, err);
117
127
  }
118
- }
119
- }, this.pollIntervalMs);
128
+ },
129
+ this.log,
130
+ this.pollIntervalMs,
131
+ );
120
132
 
121
133
  this.runningPromise.start();
122
134
  this.log.info(`Agent started with concurrency=${this.maxConcurrency}`);
@@ -133,9 +145,16 @@ export class ProverAgent implements ProverAgentApi {
133
145
  this.log.info('Agent stopped');
134
146
  }
135
147
 
148
+ @trackSpan('ProverAgent.work', (_jobSoure, job) => ({
149
+ [Attributes.PROVING_JOB_ID]: job.id,
150
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[job.type],
151
+ }))
136
152
  private async work(jobSource: ProvingJobSource, job: ProvingJob): Promise<void> {
137
153
  try {
138
- this.log.debug(`Picked up proving job id=${job.id} type=${ProvingRequestType[job.type]}`);
154
+ this.log.debug(`Picked up proving job ${job.id} ${ProvingRequestType[job.type]}`, {
155
+ jobId: job.id,
156
+ jobType: ProvingRequestType[job.type],
157
+ });
139
158
  const type = job.type;
140
159
  const inputs = await this.proofInputsDatabase.getProofInput(job.inputsUri);
141
160
  const [time, result] = await elapsed(this.getProof(inputs));
@@ -4,7 +4,6 @@ import {
4
4
  type EpochProver,
5
5
  type EpochProverManager,
6
6
  type ForkMerkleTreeOperations,
7
- type ProverCache,
8
7
  type ProvingJobBroker,
9
8
  type ProvingJobConsumer,
10
9
  type ProvingJobProducer,
@@ -16,13 +15,10 @@ import { createLogger } from '@aztec/foundation/log';
16
15
  import { NativeACVMSimulator } from '@aztec/simulator';
17
16
  import { type TelemetryClient } from '@aztec/telemetry-client';
18
17
 
19
- import { join } from 'path';
20
-
21
18
  import { type ProverClientConfig } from '../config.js';
22
19
  import { ProvingOrchestrator } from '../orchestrator/orchestrator.js';
23
- import { CachingBrokerFacade } from '../proving_broker/caching_broker_facade.js';
20
+ import { BrokerCircuitProverFacade } from '../proving_broker/broker_prover_facade.js';
24
21
  import { InlineProofStore } from '../proving_broker/proof_store.js';
25
- import { InMemoryProverCache } from '../proving_broker/prover_cache/memory.js';
26
22
  import { ProvingAgent } from '../proving_broker/proving_agent.js';
27
23
 
28
24
  /** Manages proving of epochs by orchestrating the proving of individual blocks relying on a pool of prover agents. */
@@ -30,8 +26,6 @@ export class ProverClient implements EpochProverManager {
30
26
  private running = false;
31
27
  private agents: ProvingAgent[] = [];
32
28
 
33
- private cacheDir?: string;
34
-
35
29
  private constructor(
36
30
  private config: ProverClientConfig,
37
31
  private worldState: ForkMerkleTreeOperations,
@@ -42,13 +36,12 @@ export class ProverClient implements EpochProverManager {
42
36
  ) {
43
37
  // TODO(palla/prover-node): Cache the paddingTx here, and not in each proving orchestrator,
44
38
  // so it can be reused across multiple ones and not recomputed every time.
45
- this.cacheDir = this.config.cacheDir ? join(this.config.cacheDir, `tx_prover_${this.config.proverId}`) : undefined;
46
39
  }
47
40
 
48
- public createEpochProver(cache: ProverCache = new InMemoryProverCache()): EpochProver {
41
+ public createEpochProver(): EpochProver {
49
42
  return new ProvingOrchestrator(
50
43
  this.worldState,
51
- new CachingBrokerFacade(this.orchestratorClient, cache),
44
+ new BrokerCircuitProverFacade(this.orchestratorClient),
52
45
  this.telemetry,
53
46
  this.config.proverId,
54
47
  );
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type ProofAndVerificationKey,
3
- type ProverCache,
4
3
  type ProvingJobId,
5
4
  type ProvingJobInputsMap,
6
5
  type ProvingJobProducer,
@@ -12,47 +11,45 @@ import {
12
11
  import {
13
12
  type AVM_PROOF_LENGTH_IN_FIELDS,
14
13
  type AvmCircuitInputs,
15
- type BaseOrMergeRollupPublicInputs,
16
14
  type BaseParityInputs,
15
+ type KernelCircuitPublicInputs,
16
+ type NESTED_RECURSIVE_PROOF_LENGTH,
17
+ type ParityPublicInputs,
18
+ type PrivateKernelEmptyInputData,
19
+ type RECURSIVE_PROOF_LENGTH,
20
+ type RootParityInputs,
21
+ type TUBE_PROOF_LENGTH,
22
+ } from '@aztec/circuits.js';
23
+ import {
24
+ type BaseOrMergeRollupPublicInputs,
17
25
  type BlockMergeRollupInputs,
18
26
  type BlockRootOrBlockMergePublicInputs,
19
27
  type BlockRootRollupInputs,
20
28
  type EmptyBlockRootRollupInputs,
21
- type KernelCircuitPublicInputs,
22
29
  type MergeRollupInputs,
23
- type NESTED_RECURSIVE_PROOF_LENGTH,
24
- type ParityPublicInputs,
25
30
  type PrivateBaseRollupInputs,
26
- type PrivateKernelEmptyInputData,
27
31
  type PublicBaseRollupInputs,
28
- type RECURSIVE_PROOF_LENGTH,
29
- type RootParityInputs,
30
32
  type RootRollupInputs,
31
33
  type RootRollupPublicInputs,
32
- type TUBE_PROOF_LENGTH,
33
34
  type TubeInputs,
34
- } from '@aztec/circuits.js';
35
+ } from '@aztec/circuits.js/rollup';
35
36
  import { sha256 } from '@aztec/foundation/crypto';
36
37
  import { createLogger } from '@aztec/foundation/log';
37
38
  import { retryUntil } from '@aztec/foundation/retry';
39
+ import { truncate } from '@aztec/foundation/string';
38
40
 
39
41
  import { InlineProofStore, type ProofStore } from './proof_store.js';
40
- import { InMemoryProverCache } from './prover_cache/memory.js';
41
42
 
42
43
  // 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
43
44
  const MAX_WAIT_MS = 1_200_000;
44
45
 
45
- /**
46
- * A facade around a job broker that generates stable job ids and caches results
47
- */
48
- export class CachingBrokerFacade implements ServerCircuitProver {
46
+ export class BrokerCircuitProverFacade implements ServerCircuitProver {
49
47
  constructor(
50
48
  private broker: ProvingJobProducer,
51
- private cache: ProverCache = new InMemoryProverCache(),
52
49
  private proofStore: ProofStore = new InlineProofStore(),
53
50
  private waitTimeoutMs = MAX_WAIT_MS,
54
51
  private pollIntervalMs = 1000,
55
- private log = createLogger('prover-client:caching-prover-broker'),
52
+ private log = createLogger('prover-client:broker-circuit-prover-facade'),
56
53
  ) {}
57
54
 
58
55
  private async enqueueAndWaitForJob<T extends ProvingRequestType>(
@@ -62,49 +59,23 @@ export class CachingBrokerFacade implements ServerCircuitProver {
62
59
  epochNumber = 0,
63
60
  signal?: AbortSignal,
64
61
  ): Promise<ProvingJobResultsMap[T]> {
65
- // first try the cache
66
- let jobEnqueued = false;
67
- try {
68
- const cachedResult = await this.cache.getProvingJobStatus(id);
69
- if (cachedResult.status !== 'not-found') {
70
- this.log.debug(`Found cached result for job=${id}: status=${cachedResult.status}`);
71
- }
72
-
73
- if (cachedResult.status === 'fulfilled') {
74
- const output = await this.proofStore.getProofOutput(cachedResult.value);
75
- if (output.type === type) {
76
- return output.result as ProvingJobResultsMap[T];
77
- } else {
78
- this.log.warn(`Cached result type mismatch for job=${id}. Expected=${type} but got=${output.type}`);
79
- }
80
- } else if (cachedResult.status === 'rejected') {
81
- // prefer returning a rejected promises so that we don't trigger the catch block below
82
- return Promise.reject(new Error(cachedResult.reason));
83
- } else if (cachedResult.status === 'in-progress' || cachedResult.status === 'in-queue') {
84
- jobEnqueued = true;
85
- } else {
86
- jobEnqueued = false;
87
- }
88
- } catch (err) {
89
- this.log.warn(`Failed to get cached proving job id=${id}: ${err}. Re-running job`);
90
- }
62
+ const inputsUri = await this.proofStore.saveProofInput(id, type, inputs);
63
+ await this.broker.enqueueProvingJob({
64
+ id,
65
+ type,
66
+ inputsUri,
67
+ epochNumber,
68
+ });
91
69
 
92
- if (!jobEnqueued) {
93
- try {
94
- const inputsUri = await this.proofStore.saveProofInput(id, type, inputs);
95
- await this.broker.enqueueProvingJob({
96
- id,
97
- type,
98
- inputsUri,
99
- epochNumber,
100
- });
101
- await this.cache.setProvingJobStatus(id, { status: 'in-queue' });
102
- } catch (err) {
103
- this.log.error(`Failed to enqueue proving job id=${id}: ${err}`);
104
- await this.cache.setProvingJobStatus(id, { status: 'not-found' });
105
- throw err;
106
- }
107
- }
70
+ this.log.verbose(
71
+ `Sent proving job to broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`,
72
+ {
73
+ provingJobId: id,
74
+ provingJobType: ProvingRequestType[type],
75
+ epochNumber,
76
+ inputsUri: truncate(inputsUri),
77
+ },
78
+ );
108
79
 
109
80
  // notify broker of cancelled job
110
81
  const abortFn = async () => {
@@ -131,26 +102,18 @@ export class CachingBrokerFacade implements ServerCircuitProver {
131
102
  this.pollIntervalMs / 1000,
132
103
  );
133
104
 
134
- try {
135
- await this.cache.setProvingJobStatus(id, result);
136
- } catch (err) {
137
- this.log.warn(`Failed to cache proving job id=${id} resultStatus=${result.status}: ${err}`);
138
- }
139
-
140
105
  if (result.status === 'fulfilled') {
141
106
  const output = await this.proofStore.getProofOutput(result.value);
142
107
  if (output.type === type) {
143
108
  return output.result as ProvingJobResultsMap[T];
144
109
  } else {
145
- return Promise.reject(new Error(`Unexpected proof type: ${output.type}. Expected: ${type}`));
110
+ throw new Error(`Unexpected proof type: ${output.type}. Expected: ${type}`);
146
111
  }
147
112
  } else {
148
- return Promise.reject(new Error(result.reason));
113
+ throw new Error(result.reason);
149
114
  }
150
115
  } finally {
151
116
  signal?.removeEventListener('abort', abortFn);
152
- // we've saved the result in our cache. We can tell the broker to clear its state
153
- await this.broker.cleanUpProvingJobState(id);
154
117
  }
155
118
  }
156
119
 
@@ -160,7 +123,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
160
123
  epochNumber?: number,
161
124
  ): Promise<ProofAndVerificationKey<typeof AVM_PROOF_LENGTH_IN_FIELDS>> {
162
125
  return this.enqueueAndWaitForJob(
163
- this.generateId(ProvingRequestType.PUBLIC_VM, inputs),
126
+ this.generateId(ProvingRequestType.PUBLIC_VM, inputs, epochNumber),
164
127
  ProvingRequestType.PUBLIC_VM,
165
128
  inputs,
166
129
  epochNumber,
@@ -174,7 +137,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
174
137
  epochNumber?: number,
175
138
  ): Promise<PublicInputsAndRecursiveProof<ParityPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
176
139
  return this.enqueueAndWaitForJob(
177
- this.generateId(ProvingRequestType.BASE_PARITY, inputs),
140
+ this.generateId(ProvingRequestType.BASE_PARITY, inputs, epochNumber),
178
141
  ProvingRequestType.BASE_PARITY,
179
142
  inputs,
180
143
  epochNumber,
@@ -188,7 +151,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
188
151
  epochNumber?: number,
189
152
  ): Promise<PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
190
153
  return this.enqueueAndWaitForJob(
191
- this.generateId(ProvingRequestType.BLOCK_MERGE_ROLLUP, input),
154
+ this.generateId(ProvingRequestType.BLOCK_MERGE_ROLLUP, input, epochNumber),
192
155
  ProvingRequestType.BLOCK_MERGE_ROLLUP,
193
156
  input,
194
157
  epochNumber,
@@ -202,7 +165,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
202
165
  epochNumber?: number,
203
166
  ): Promise<PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
204
167
  return this.enqueueAndWaitForJob(
205
- this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input),
168
+ this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input, epochNumber),
206
169
  ProvingRequestType.BLOCK_ROOT_ROLLUP,
207
170
  input,
208
171
  epochNumber,
@@ -216,7 +179,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
216
179
  epochNumber?: number,
217
180
  ): Promise<PublicInputsAndRecursiveProof<BlockRootOrBlockMergePublicInputs>> {
218
181
  return this.enqueueAndWaitForJob(
219
- this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input),
182
+ this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input, epochNumber),
220
183
  ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP,
221
184
  input,
222
185
  epochNumber,
@@ -230,7 +193,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
230
193
  epochNumber?: number,
231
194
  ): Promise<PublicInputsAndRecursiveProof<KernelCircuitPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
232
195
  return this.enqueueAndWaitForJob(
233
- this.generateId(ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs),
196
+ this.generateId(ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs, epochNumber),
234
197
  ProvingRequestType.PRIVATE_KERNEL_EMPTY,
235
198
  inputs,
236
199
  epochNumber,
@@ -244,7 +207,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
244
207
  epochNumber?: number,
245
208
  ): Promise<PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
246
209
  return this.enqueueAndWaitForJob(
247
- this.generateId(ProvingRequestType.MERGE_ROLLUP, input),
210
+ this.generateId(ProvingRequestType.MERGE_ROLLUP, input, epochNumber),
248
211
  ProvingRequestType.MERGE_ROLLUP,
249
212
  input,
250
213
  epochNumber,
@@ -257,7 +220,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
257
220
  epochNumber?: number,
258
221
  ): Promise<PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
259
222
  return this.enqueueAndWaitForJob(
260
- this.generateId(ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput),
223
+ this.generateId(ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput, epochNumber),
261
224
  ProvingRequestType.PRIVATE_BASE_ROLLUP,
262
225
  baseRollupInput,
263
226
  epochNumber,
@@ -271,7 +234,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
271
234
  epochNumber?: number,
272
235
  ): Promise<PublicInputsAndRecursiveProof<BaseOrMergeRollupPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
273
236
  return this.enqueueAndWaitForJob(
274
- this.generateId(ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs),
237
+ this.generateId(ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs, epochNumber),
275
238
  ProvingRequestType.PUBLIC_BASE_ROLLUP,
276
239
  inputs,
277
240
  epochNumber,
@@ -285,7 +248,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
285
248
  epochNumber?: number,
286
249
  ): Promise<PublicInputsAndRecursiveProof<ParityPublicInputs, typeof NESTED_RECURSIVE_PROOF_LENGTH>> {
287
250
  return this.enqueueAndWaitForJob(
288
- this.generateId(ProvingRequestType.ROOT_PARITY, inputs),
251
+ this.generateId(ProvingRequestType.ROOT_PARITY, inputs, epochNumber),
289
252
  ProvingRequestType.ROOT_PARITY,
290
253
  inputs,
291
254
  epochNumber,
@@ -299,7 +262,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
299
262
  epochNumber?: number,
300
263
  ): Promise<PublicInputsAndRecursiveProof<RootRollupPublicInputs, typeof RECURSIVE_PROOF_LENGTH>> {
301
264
  return this.enqueueAndWaitForJob(
302
- this.generateId(ProvingRequestType.ROOT_ROLLUP, input),
265
+ this.generateId(ProvingRequestType.ROOT_ROLLUP, input, epochNumber),
303
266
  ProvingRequestType.ROOT_ROLLUP,
304
267
  input,
305
268
  epochNumber,
@@ -313,7 +276,7 @@ export class CachingBrokerFacade implements ServerCircuitProver {
313
276
  epochNumber?: number,
314
277
  ): Promise<ProofAndVerificationKey<typeof TUBE_PROOF_LENGTH>> {
315
278
  return this.enqueueAndWaitForJob(
316
- this.generateId(ProvingRequestType.TUBE_PROOF, tubeInput),
279
+ this.generateId(ProvingRequestType.TUBE_PROOF, tubeInput, epochNumber),
317
280
  ProvingRequestType.TUBE_PROOF,
318
281
  tubeInput,
319
282
  epochNumber,
@@ -321,8 +284,8 @@ export class CachingBrokerFacade implements ServerCircuitProver {
321
284
  );
322
285
  }
323
286
 
324
- private generateId(type: ProvingRequestType, inputs: { toBuffer(): Buffer }) {
287
+ private generateId(type: ProvingRequestType, inputs: { toBuffer(): Buffer }, epochNumber = 0) {
325
288
  const inputsHash = sha256(inputs.toBuffer());
326
- return `${ProvingRequestType[type]}:${inputsHash.toString('hex')}`;
289
+ return `${epochNumber}:${ProvingRequestType[type]}:${inputsHash.toString('hex')}`;
327
290
  }
328
291
  }
@@ -10,8 +10,9 @@ import {
10
10
  } from '@aztec/circuit-types';
11
11
  import { createLogger } from '@aztec/foundation/log';
12
12
  import { RunningPromise } from '@aztec/foundation/running-promise';
13
+ import { truncate } from '@aztec/foundation/string';
13
14
  import { Timer } from '@aztec/foundation/timer';
14
- import { type TelemetryClient } from '@aztec/telemetry-client';
15
+ import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
15
16
 
16
17
  import { type ProofStore } from './proof_store.js';
17
18
  import { ProvingAgentInstrumentation } from './proving_agent_instrumentation.js';
@@ -20,12 +21,14 @@ import { ProvingJobController, ProvingJobControllerStatus } from './proving_job_
20
21
  /**
21
22
  * A helper class that encapsulates a circuit prover and connects it to a job source.
22
23
  */
23
- export class ProvingAgent {
24
+ export class ProvingAgent implements Traceable {
24
25
  private currentJobController?: ProvingJobController;
25
26
  private runningPromise: RunningPromise;
26
27
  private instrumentation: ProvingAgentInstrumentation;
27
28
  private idleTimer: Timer | undefined;
28
29
 
30
+ public readonly tracer: Tracer;
31
+
29
32
  constructor(
30
33
  /** The source of proving jobs */
31
34
  private broker: ProvingJobConsumer,
@@ -41,8 +44,9 @@ export class ProvingAgent {
41
44
  private pollIntervalMs = 1000,
42
45
  private log = createLogger('prover-client:proving-agent'),
43
46
  ) {
47
+ this.tracer = client.getTracer('ProvingAgent');
44
48
  this.instrumentation = new ProvingAgentInstrumentation(client);
45
- this.runningPromise = new RunningPromise(this.safeWork, this.pollIntervalMs);
49
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollIntervalMs);
46
50
  }
47
51
 
48
52
  public setCircuitProver(circuitProver: ServerCircuitProver): void {
@@ -63,76 +67,74 @@ export class ProvingAgent {
63
67
  await this.runningPromise.stop();
64
68
  }
65
69
 
66
- private safeWork = async () => {
67
- try {
68
- // every tick we need to
69
- // (1) either do a heartbeat, telling the broker that we're working
70
- // (2) get a new job
71
- // If during (1) the broker returns a new job that means we can cancel the current job and start the new one
72
- let maybeJob: { job: ProvingJob; time: number } | undefined;
73
- if (this.currentJobController?.getStatus() === ProvingJobControllerStatus.PROVING) {
74
- maybeJob = await this.broker.reportProvingJobProgress(
75
- this.currentJobController.getJobId(),
76
- this.currentJobController.getStartedAt(),
77
- { allowList: this.proofAllowList },
78
- );
79
- } else {
80
- maybeJob = await this.broker.getProvingJob({ allowList: this.proofAllowList });
81
- }
82
-
83
- if (!maybeJob) {
84
- return;
85
- }
86
-
87
- let abortedProofJobId: string | undefined;
88
- let abortedProofName: string | undefined;
89
- if (this.currentJobController?.getStatus() === ProvingJobControllerStatus.PROVING) {
90
- abortedProofJobId = this.currentJobController.getJobId();
91
- abortedProofName = this.currentJobController.getProofTypeName();
92
- this.currentJobController?.abort();
93
- }
94
-
95
- const { job, time } = maybeJob;
96
- let inputs: ProvingJobInputs;
97
- try {
98
- inputs = await this.proofStore.getProofInput(job.inputsUri);
99
- } catch (err) {
100
- await this.broker.reportProvingJobError(job.id, 'Failed to load proof inputs', true);
101
- return;
102
- }
103
-
104
- this.currentJobController = new ProvingJobController(
105
- job.id,
106
- inputs,
107
- time,
108
- this.circuitProver,
109
- this.handleJobResult,
70
+ @trackSpan('ProvingAgent.safeWork')
71
+ private async work() {
72
+ // every tick we need to
73
+ // (1) either do a heartbeat, telling the broker that we're working
74
+ // (2) get a new job
75
+ // If during (1) the broker returns a new job that means we can cancel the current job and start the new one
76
+ let maybeJob: { job: ProvingJob; time: number } | undefined;
77
+ if (this.currentJobController?.getStatus() === ProvingJobControllerStatus.PROVING) {
78
+ maybeJob = await this.broker.reportProvingJobProgress(
79
+ this.currentJobController.getJobId(),
80
+ this.currentJobController.getStartedAt(),
81
+ { allowList: this.proofAllowList },
110
82
  );
83
+ } else {
84
+ maybeJob = await this.broker.getProvingJob({ allowList: this.proofAllowList });
85
+ }
86
+
87
+ if (!maybeJob) {
88
+ return;
89
+ }
90
+
91
+ let abortedProofJobId: string | undefined;
92
+ let abortedProofName: string | undefined;
93
+ if (this.currentJobController?.getStatus() === ProvingJobControllerStatus.PROVING) {
94
+ abortedProofJobId = this.currentJobController.getJobId();
95
+ abortedProofName = this.currentJobController.getProofTypeName();
96
+ this.currentJobController?.abort();
97
+ }
111
98
 
112
- if (abortedProofJobId) {
113
- this.log.info(
114
- `Aborting job id=${abortedProofJobId} type=${abortedProofName} to start new job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncateString(
115
- job.inputsUri,
116
- )}`,
117
- );
118
- } else {
119
- this.log.info(
120
- `Starting job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncateString(
121
- job.inputsUri,
122
- )}`,
123
- );
124
- }
125
-
126
- if (this.idleTimer) {
127
- this.instrumentation.recordIdleTime(this.idleTimer);
128
- }
129
- this.idleTimer = undefined;
130
-
131
- this.currentJobController.start();
99
+ const { job, time } = maybeJob;
100
+ let inputs: ProvingJobInputs;
101
+ try {
102
+ inputs = await this.proofStore.getProofInput(job.inputsUri);
132
103
  } catch (err) {
133
- this.log.error(`Error in ProvingAgent: ${String(err)}`);
104
+ await this.broker.reportProvingJobError(job.id, 'Failed to load proof inputs', true);
105
+ return;
134
106
  }
135
- };
107
+
108
+ this.currentJobController = new ProvingJobController(
109
+ job.id,
110
+ inputs,
111
+ job.epochNumber,
112
+ time,
113
+ this.circuitProver,
114
+ this.handleJobResult,
115
+ );
116
+
117
+ if (abortedProofJobId) {
118
+ this.log.info(
119
+ `Aborting job id=${abortedProofJobId} type=${abortedProofName} to start new job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncate(
120
+ job.inputsUri,
121
+ )}`,
122
+ );
123
+ } else {
124
+ this.log.info(
125
+ `Starting job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncate(
126
+ job.inputsUri,
127
+ )}`,
128
+ );
129
+ }
130
+
131
+ if (this.idleTimer) {
132
+ this.instrumentation.recordIdleTime(this.idleTimer);
133
+ }
134
+ this.idleTimer = undefined;
135
+
136
+ this.currentJobController.start();
137
+ }
136
138
 
137
139
  handleJobResult = async <T extends ProvingRequestType>(
138
140
  jobId: ProvingJobId,
@@ -147,14 +149,8 @@ export class ProvingAgent {
147
149
  return this.broker.reportProvingJobError(jobId, err.message, retry);
148
150
  } else if (result) {
149
151
  const outputUri = await this.proofStore.saveProofOutput(jobId, type, result);
150
- this.log.info(
151
- `Job id=${jobId} type=${ProvingRequestType[type]} completed outputUri=${truncateString(outputUri)}`,
152
- );
152
+ this.log.info(`Job id=${jobId} type=${ProvingRequestType[type]} completed outputUri=${truncate(outputUri)}`);
153
153
  return this.broker.reportProvingJobSuccess(jobId, outputUri);
154
154
  }
155
155
  };
156
156
  }
157
-
158
- function truncateString(str: string, length: number = 64): string {
159
- return str.length > length ? str.slice(0, length) + '...' : str;
160
- }