@aztec/prover-client 0.43.0 → 0.45.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.
@@ -9,6 +9,7 @@ import {
9
9
  type TxEffect,
10
10
  makeEmptyProcessedTx,
11
11
  makePaddingProcessedTx,
12
+ mapPublicKernelToCircuitName,
12
13
  toTxEffect,
13
14
  } from '@aztec/circuit-types';
14
15
  import {
@@ -20,6 +21,7 @@ import {
20
21
  type PublicInputsAndRecursiveProof,
21
22
  type ServerCircuitProver,
22
23
  } from '@aztec/circuit-types/interfaces';
24
+ import { type CircuitName } from '@aztec/circuit-types/stats';
23
25
  import {
24
26
  AGGREGATION_OBJECT_LENGTH,
25
27
  AvmCircuitInputs,
@@ -53,6 +55,7 @@ import { createDebugLogger } from '@aztec/foundation/log';
53
55
  import { promiseWithResolvers } from '@aztec/foundation/promise';
54
56
  import { BufferReader, type Tuple } from '@aztec/foundation/serialize';
55
57
  import { pushTestData } from '@aztec/foundation/testing';
58
+ import { Attributes, type TelemetryClient, type Tracer, trackSpan, wrapCallbackInSpan } from '@aztec/telemetry-client';
56
59
  import { type MerkleTreeOperations } from '@aztec/world-state';
57
60
 
58
61
  import { inspect } from 'util';
@@ -91,7 +94,16 @@ export class ProvingOrchestrator {
91
94
  private pendingProvingJobs: AbortController[] = [];
92
95
  private paddingTx: PaddingProcessedTx | undefined = undefined;
93
96
 
94
- constructor(private db: MerkleTreeOperations, private prover: ServerCircuitProver, private initialHeader?: Header) {}
97
+ public readonly tracer: Tracer;
98
+
99
+ constructor(
100
+ private db: MerkleTreeOperations,
101
+ private prover: ServerCircuitProver,
102
+ telemetryClient: TelemetryClient,
103
+ private initialHeader?: Header,
104
+ ) {
105
+ this.tracer = telemetryClient.getTracer('ProvingOrchestrator');
106
+ }
95
107
 
96
108
  /**
97
109
  * Resets the orchestrator's cached padding tx.
@@ -108,6 +120,10 @@ export class ProvingOrchestrator {
108
120
  * @param verificationKeys - The private kernel verification keys
109
121
  * @returns A proving ticket, containing a promise notifying of proving completion
110
122
  */
123
+ @trackSpan('ProvingOrchestrator.startNewBlock', (numTxs, globalVariables) => ({
124
+ [Attributes.BLOCK_SIZE]: numTxs,
125
+ [Attributes.BLOCK_NUMBER]: globalVariables.blockNumber.toNumber(),
126
+ }))
111
127
  public async startNewBlock(
112
128
  numTxs: number,
113
129
  globalVariables: GlobalVariables,
@@ -119,10 +135,8 @@ export class ProvingOrchestrator {
119
135
  this.initialHeader = await this.db.buildInitialHeader();
120
136
  }
121
137
 
122
- // Check that the length of the array of txs is a power of two
123
- // See https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
124
- if (!Number.isInteger(numTxs) || numTxs < 2 || (numTxs & (numTxs - 1)) !== 0) {
125
- throw new Error(`Length of txs for the block should be a power of two and at least two (got ${numTxs})`);
138
+ if (!Number.isInteger(numTxs) || numTxs < 2) {
139
+ throw new Error(`Length of txs for the block should be at least two (got ${numTxs})`);
126
140
  }
127
141
  // Cancel any currently proving block before starting a new one
128
142
  this.cancelBlock();
@@ -193,6 +207,9 @@ export class ProvingOrchestrator {
193
207
  * The interface to add a simulated transaction to the scheduler
194
208
  * @param tx - The transaction to be proven
195
209
  */
210
+ @trackSpan('ProvingOrchestrator.addNewTx', tx => ({
211
+ [Attributes.TX_HASH]: tx.hash.toString(),
212
+ }))
196
213
  public async addNewTx(tx: ProcessedTx): Promise<void> {
197
214
  if (!this.provingState) {
198
215
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions`);
@@ -206,13 +223,29 @@ export class ProvingOrchestrator {
206
223
 
207
224
  logger.info(`Received transaction: ${tx.hash}`);
208
225
 
226
+ if (tx.isEmpty) {
227
+ logger.warn(`Ignoring empty transaction ${tx.hash} - it will not be added to this block`);
228
+ return;
229
+ }
230
+
209
231
  const [inputs, treeSnapshots] = await this.prepareTransaction(tx, this.provingState);
210
232
  this.enqueueFirstProof(inputs, treeSnapshots, tx, this.provingState);
211
233
  }
212
234
 
213
235
  /**
214
- * Marks the block as full and pads it to the full power of 2 block size, no more transactions will be accepted.
236
+ * Marks the block as full and pads it if required, no more transactions will be accepted.
215
237
  */
238
+ @trackSpan('ProvingOrchestrator.setBlockCompleted', function () {
239
+ if (!this.provingState) {
240
+ return {};
241
+ }
242
+
243
+ return {
244
+ [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(),
245
+ [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs,
246
+ [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived,
247
+ };
248
+ })
216
249
  public async setBlockCompleted() {
217
250
  if (!this.provingState) {
218
251
  throw new Error(`Invalid proving state, call startNewBlock before adding transactions or completing the block`);
@@ -222,10 +255,15 @@ export class ProvingOrchestrator {
222
255
  const paddingTxCount = this.provingState.totalNumTxs - this.provingState.transactionsReceived;
223
256
  if (paddingTxCount === 0) {
224
257
  return;
258
+ } else if (this.provingState.totalNumTxs > 2) {
259
+ throw new Error(`Block not ready for completion: expecting ${paddingTxCount} more transactions.`);
225
260
  }
226
261
 
227
262
  logger.debug(`Padding rollup with ${paddingTxCount} empty transactions`);
228
263
  // Make an empty padding transaction
264
+ // Required for:
265
+ // 0 (when we want an empty block, largely for testing), or
266
+ // 1 (we need to pad with one tx as all rollup circuits require a pair of inputs) txs
229
267
  // Insert it into the tree the required number of times to get all of the
230
268
  // base rollup inputs
231
269
  // Then enqueue the proving of all the transactions
@@ -264,18 +302,26 @@ export class ProvingOrchestrator {
264
302
  logger.debug(`Enqueuing deferred proving for padding txs to enqueue ${txInputs.length} paddings`);
265
303
  this.deferredProving(
266
304
  provingState,
267
- signal =>
268
- this.prover.getEmptyPrivateKernelProof(
269
- {
270
- // Chain id and version should not change even if the proving state does, so it's safe to use them for the padding tx
271
- // which gets cached across multiple runs of the orchestrator with different proving states. If they were to change,
272
- // we'd have to clear out the paddingTx here and regenerate it when they do.
273
- chainId: unprovenPaddingTx.data.constants.txContext.chainId,
274
- version: unprovenPaddingTx.data.constants.txContext.version,
275
- header: unprovenPaddingTx.data.constants.historicalHeader,
276
- },
277
- signal,
278
- ),
305
+ wrapCallbackInSpan(
306
+ this.tracer,
307
+ 'ProvingOrchestrator.prover.getEmptyPrivateKernelProof',
308
+ {
309
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
310
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' as CircuitName,
311
+ },
312
+ signal =>
313
+ this.prover.getEmptyPrivateKernelProof(
314
+ {
315
+ // Chain id and version should not change even if the proving state does, so it's safe to use them for the padding tx
316
+ // which gets cached across multiple runs of the orchestrator with different proving states. If they were to change,
317
+ // we'd have to clear out the paddingTx here and regenerate it when they do.
318
+ chainId: unprovenPaddingTx.data.constants.txContext.chainId,
319
+ version: unprovenPaddingTx.data.constants.txContext.version,
320
+ header: unprovenPaddingTx.data.constants.historicalHeader,
321
+ },
322
+ signal,
323
+ ),
324
+ ),
279
325
  result => {
280
326
  logger.debug(`Completed proof for padding tx, now enqueuing ${txInputs.length} padding txs`);
281
327
  this.paddingTx = makePaddingProcessedTx(result);
@@ -319,6 +365,13 @@ export class ProvingOrchestrator {
319
365
  * Performs the final tree update for the block and returns the fully proven block.
320
366
  * @returns The fully proven block and proof.
321
367
  */
368
+ @trackSpan('ProvingOrchestrator.finaliseBlock', function () {
369
+ return {
370
+ [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(),
371
+ [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived,
372
+ [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs,
373
+ };
374
+ })
322
375
  public async finaliseBlock() {
323
376
  try {
324
377
  if (
@@ -496,6 +549,9 @@ export class ProvingOrchestrator {
496
549
  }
497
550
 
498
551
  // Updates the merkle trees for a transaction. The first enqueued job for a transaction
552
+ @trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', (_, tx) => ({
553
+ [Attributes.TX_HASH]: tx.hash.toString(),
554
+ }))
499
555
  private async prepareBaseRollupInputs(
500
556
  provingState: ProvingState | undefined,
501
557
  tx: ProcessedTx,
@@ -531,13 +587,18 @@ export class ProvingOrchestrator {
531
587
  VerificationKeyAsFields,
532
588
  ],
533
589
  ) {
534
- const mergeLevel = currentLevel - 1n;
535
- const indexWithinMergeLevel = currentIndex >> 1n;
590
+ const [mergeLevel, indexWithinMergeLevel, indexWithinMerge] = provingState.findMergeLevel(
591
+ currentLevel,
592
+ currentIndex,
593
+ );
536
594
  const mergeIndex = 2n ** mergeLevel - 1n + indexWithinMergeLevel;
537
- const subscript = Number(mergeIndex);
538
- const indexWithinMerge = Number(currentIndex & 1n);
539
- const ready = provingState.storeMergeInputs(mergeInputs, indexWithinMerge, subscript);
540
- return { ready, indexWithinMergeLevel, mergeLevel, mergeInputData: provingState.getMergeInputs(subscript) };
595
+ const ready = provingState.storeMergeInputs(mergeInputs, Number(indexWithinMerge), Number(mergeIndex));
596
+ return {
597
+ ready,
598
+ indexWithinMergeLevel,
599
+ mergeLevel,
600
+ mergeInputData: provingState.getMergeInputs(Number(mergeIndex)),
601
+ };
541
602
  }
542
603
 
543
604
  // Executes the base rollup circuit and stored the output as intermediate state for the parent merge/root circuit
@@ -593,7 +654,16 @@ export class ProvingOrchestrator {
593
654
 
594
655
  this.deferredProving(
595
656
  provingState,
596
- signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal),
657
+ wrapCallbackInSpan(
658
+ this.tracer,
659
+ 'ProvingOrchestrator.prover.getBaseRollupProof',
660
+ {
661
+ [Attributes.TX_HASH]: tx.processedTx.hash.toString(),
662
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
663
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' as CircuitName,
664
+ },
665
+ signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal),
666
+ ),
597
667
  result => {
598
668
  logger.debug(`Completed proof for base rollup for tx ${tx.processedTx.hash.toString()}`);
599
669
  validatePartialState(result.inputs.end, tx.treeSnapshots);
@@ -622,7 +692,15 @@ export class ProvingOrchestrator {
622
692
 
623
693
  this.deferredProving(
624
694
  provingState,
625
- signal => this.prover.getMergeRollupProof(inputs, signal),
695
+ wrapCallbackInSpan(
696
+ this.tracer,
697
+ 'ProvingOrchestrator.prover.getMergeRollupProof',
698
+ {
699
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
700
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' as CircuitName,
701
+ },
702
+ signal => this.prover.getMergeRollupProof(inputs, signal),
703
+ ),
626
704
  result => {
627
705
  this.storeAndExecuteNextMergeLevel(provingState, level, index, [
628
706
  result.inputs,
@@ -658,7 +736,15 @@ export class ProvingOrchestrator {
658
736
 
659
737
  this.deferredProving(
660
738
  provingState,
661
- signal => this.prover.getRootRollupProof(inputs, signal),
739
+ wrapCallbackInSpan(
740
+ this.tracer,
741
+ 'ProvingOrchestrator.prover.getRootRollupProof',
742
+ {
743
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
744
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup' as CircuitName,
745
+ },
746
+ signal => this.prover.getRootRollupProof(inputs, signal),
747
+ ),
662
748
  result => {
663
749
  provingState.rootRollupPublicInputs = result.inputs;
664
750
  provingState.finalAggregationObject = extractAggregationObject(
@@ -680,7 +766,15 @@ export class ProvingOrchestrator {
680
766
  private enqueueBaseParityCircuit(provingState: ProvingState, inputs: BaseParityInputs, index: number) {
681
767
  this.deferredProving(
682
768
  provingState,
683
- signal => this.prover.getBaseParityProof(inputs, signal),
769
+ wrapCallbackInSpan(
770
+ this.tracer,
771
+ 'ProvingOrchestrator.prover.getBaseParityProof',
772
+ {
773
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
774
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' as CircuitName,
775
+ },
776
+ signal => this.prover.getBaseParityProof(inputs, signal),
777
+ ),
684
778
  rootInput => {
685
779
  provingState.setRootParityInputs(rootInput, index);
686
780
  if (provingState.areRootParityInputsReady()) {
@@ -701,7 +795,15 @@ export class ProvingOrchestrator {
701
795
  private enqueueRootParityCircuit(provingState: ProvingState | undefined, inputs: RootParityInputs) {
702
796
  this.deferredProving(
703
797
  provingState,
704
- signal => this.prover.getRootParityProof(inputs, signal),
798
+ wrapCallbackInSpan(
799
+ this.tracer,
800
+ 'ProvingOrchestrator.prover.getRootParityProof',
801
+ {
802
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
803
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' as CircuitName,
804
+ },
805
+ signal => this.prover.getRootParityProof(inputs, signal),
806
+ ),
705
807
  async rootInput => {
706
808
  provingState!.finalRootParityInput = rootInput;
707
809
  await this.checkAndEnqueueRootRollup(provingState);
@@ -770,25 +872,34 @@ export class ProvingOrchestrator {
770
872
  if (publicFunction.vmRequest) {
771
873
  // This function tries to do AVM proving. If there is a failure, it fakes the proof unless AVM_PROVING_STRICT is defined.
772
874
  // Nothing downstream depends on the AVM proof yet. So having this mode lets us incrementally build the AVM circuit.
773
- const doAvmProving = async (signal: AbortSignal) => {
774
- const inputs: AvmCircuitInputs = new AvmCircuitInputs(
775
- publicFunction.vmRequest!.bytecode,
776
- publicFunction.vmRequest!.calldata,
777
- publicFunction.vmRequest!.kernelRequest.inputs.publicCall.callStackItem.publicInputs,
778
- publicFunction.vmRequest!.avmHints,
779
- );
780
- try {
781
- return await this.prover.getAvmProof(inputs, signal);
782
- } catch (err) {
783
- if (process.env.AVM_PROVING_STRICT) {
784
- throw err;
785
- } else {
786
- logger.warn(`Error thrown when proving AVM circuit: ${err}`);
787
- logger.warn(`AVM_PROVING_STRICT is off, faking AVM proof and carrying on...`);
788
- return { proof: makeEmptyProof(), verificationKey: VerificationKeyData.makeFake() };
875
+ const doAvmProving = wrapCallbackInSpan(
876
+ this.tracer,
877
+ 'ProvingOrchestrator.prover.getAvmProof',
878
+ {
879
+ [Attributes.TX_HASH]: txProvingState.processedTx.hash.toString(),
880
+ [Attributes.APP_CIRCUIT_NAME]: publicFunction.vmRequest!.functionName,
881
+ },
882
+ async (signal: AbortSignal) => {
883
+ const inputs: AvmCircuitInputs = new AvmCircuitInputs(
884
+ publicFunction.vmRequest!.functionName,
885
+ publicFunction.vmRequest!.bytecode,
886
+ publicFunction.vmRequest!.calldata,
887
+ publicFunction.vmRequest!.kernelRequest.inputs.publicCall.callStackItem.publicInputs,
888
+ publicFunction.vmRequest!.avmHints,
889
+ );
890
+ try {
891
+ return await this.prover.getAvmProof(inputs, signal);
892
+ } catch (err) {
893
+ if (process.env.AVM_PROVING_STRICT) {
894
+ throw err;
895
+ } else {
896
+ logger.warn(`Error thrown when proving AVM circuit: ${err}`);
897
+ logger.warn(`AVM_PROVING_STRICT is off, faking AVM proof and carrying on...`);
898
+ return { proof: makeEmptyProof(), verificationKey: VerificationKeyData.makeFake() };
899
+ }
789
900
  }
790
- }
791
- };
901
+ },
902
+ );
792
903
  this.deferredProving(provingState, doAvmProving, proofAndVk => {
793
904
  logger.debug(`Proven VM for function index ${functionIndex} of tx index ${txIndex}`);
794
905
  this.checkAndEnqueuePublicKernel(provingState, txIndex, functionIndex, proofAndVk.proof);
@@ -834,13 +945,25 @@ export class ProvingOrchestrator {
834
945
 
835
946
  this.deferredProving(
836
947
  provingState,
837
- (signal): Promise<PublicInputsAndRecursiveProof<KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs>> => {
838
- if (request.type === PublicKernelType.TAIL) {
839
- return this.prover.getPublicTailProof(request, signal);
840
- } else {
841
- return this.prover.getPublicKernelProof(request, signal);
842
- }
843
- },
948
+ wrapCallbackInSpan(
949
+ this.tracer,
950
+ request.type === PublicKernelType.TAIL
951
+ ? 'ProvingOrchestrator.prover.getPublicTailProof'
952
+ : 'ProvingOrchestrator.prover.getPublicKernelProof',
953
+ {
954
+ [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server',
955
+ [Attributes.PROTOCOL_CIRCUIT_NAME]: mapPublicKernelToCircuitName(request.type),
956
+ },
957
+ (
958
+ signal,
959
+ ): Promise<PublicInputsAndRecursiveProof<KernelCircuitPublicInputs | PublicKernelCircuitPublicInputs>> => {
960
+ if (request.type === PublicKernelType.TAIL) {
961
+ return this.prover.getPublicTailProof(request, signal);
962
+ } else {
963
+ return this.prover.getPublicKernelProof(request, signal);
964
+ }
965
+ },
966
+ ),
844
967
  result => {
845
968
  const nextKernelRequest = txProvingState.getNextPublicKernelFromKernelProof(
846
969
  functionIndex,
@@ -879,5 +1002,9 @@ function extractAggregationObject(proof: Proof, numPublicInputs: number): Fr[] {
879
1002
  Fr.SIZE_IN_BYTES * (numPublicInputs - AGGREGATION_OBJECT_LENGTH),
880
1003
  Fr.SIZE_IN_BYTES * numPublicInputs,
881
1004
  );
1005
+ // TODO(#7159): Remove the following workaround
1006
+ if (buffer.length === 0) {
1007
+ return Array.from({ length: AGGREGATION_OBJECT_LENGTH }, () => Fr.ZERO);
1008
+ }
882
1009
  return BufferReader.asReader(buffer).readArray(AGGREGATION_OBJECT_LENGTH, Fr);
883
1010
  }
@@ -71,6 +71,32 @@ export class ProvingState {
71
71
  return BigInt(Math.ceil(Math.log2(this.totalNumTxs)) - 1);
72
72
  }
73
73
 
74
+ // Calculates the index and level of the parent rollup circuit
75
+ // Based on tree implementation in unbalanced_tree.ts -> batchInsert()
76
+ public findMergeLevel(currentLevel: bigint, currentIndex: bigint) {
77
+ const moveUpMergeLevel = (levelSize: number, index: bigint, nodeToShift: boolean) => {
78
+ levelSize /= 2;
79
+ if (levelSize & 1) {
80
+ [levelSize, nodeToShift] = nodeToShift ? [levelSize + 1, false] : [levelSize - 1, true];
81
+ }
82
+ index >>= 1n;
83
+ return { thisLevelSize: levelSize, thisIndex: index, shiftUp: nodeToShift };
84
+ };
85
+ let [thisLevelSize, shiftUp] = this.totalNumTxs & 1 ? [this.totalNumTxs - 1, true] : [this.totalNumTxs, false];
86
+ const maxLevel = this.numMergeLevels + 1n;
87
+ let placeholder = currentIndex;
88
+ for (let i = 0; i < maxLevel - currentLevel; i++) {
89
+ ({ thisLevelSize, thisIndex: placeholder, shiftUp } = moveUpMergeLevel(thisLevelSize, placeholder, shiftUp));
90
+ }
91
+ let thisIndex = currentIndex;
92
+ let mergeLevel = currentLevel;
93
+ while (thisIndex >= thisLevelSize && mergeLevel != 0n) {
94
+ mergeLevel -= 1n;
95
+ ({ thisLevelSize, thisIndex, shiftUp } = moveUpMergeLevel(thisLevelSize, thisIndex, shiftUp));
96
+ }
97
+ return [mergeLevel - 1n, thisIndex >> 1n, thisIndex & 1n];
98
+ }
99
+
74
100
  // Adds a transaction to the proving state, returns it's index
75
101
  // Will update the proving life cycle if this is the last transaction
76
102
  public addNewTx(tx: TxProvingState) {
@@ -9,6 +9,7 @@ import {
9
9
  } from '@aztec/circuit-types/interfaces';
10
10
  import { type Fr, type GlobalVariables, type Header, type VerificationKeys } from '@aztec/circuits.js';
11
11
  import { NativeACVMSimulator } from '@aztec/simulator';
12
+ import { type TelemetryClient } from '@aztec/telemetry-client';
12
13
  import { type WorldStateSynchronizer } from '@aztec/world-state';
13
14
 
14
15
  import { type ProverClientConfig } from '../config.js';
@@ -28,11 +29,17 @@ export class TxProver implements ProverClient {
28
29
  private config: ProverClientConfig,
29
30
  private worldStateSynchronizer: WorldStateSynchronizer,
30
31
  private vks: VerificationKeys,
32
+ private telemetry: TelemetryClient,
31
33
  private agent?: ProverAgent,
32
34
  initialHeader?: Header,
33
35
  ) {
34
36
  this.queue = new MemoryProvingQueue(config.proverJobTimeoutMs, config.proverJobPollIntervalMs);
35
- this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.queue, initialHeader);
37
+ this.orchestrator = new ProvingOrchestrator(
38
+ worldStateSynchronizer.getLatest(),
39
+ this.queue,
40
+ telemetry,
41
+ initialHeader,
42
+ );
36
43
  }
37
44
 
38
45
  async updateProverConfig(config: Partial<ProverClientConfig & { vks: VerificationKeys }>): Promise<void> {
@@ -43,7 +50,7 @@ export class TxProver implements ProverClient {
43
50
  }
44
51
 
45
52
  if (newConfig.realProofs !== this.config.realProofs && this.agent) {
46
- const circuitProver = await TxProver.buildCircuitProver(newConfig);
53
+ const circuitProver = await TxProver.buildCircuitProver(newConfig, this.telemetry);
47
54
  this.agent.setCircuitProver(circuitProver);
48
55
  }
49
56
 
@@ -95,31 +102,35 @@ export class TxProver implements ProverClient {
95
102
  config: ProverClientConfig,
96
103
  vks: VerificationKeys,
97
104
  worldStateSynchronizer: WorldStateSynchronizer,
105
+ telemetry: TelemetryClient,
98
106
  initialHeader?: Header,
99
107
  ) {
100
108
  const agent = config.proverAgentEnabled
101
109
  ? new ProverAgent(
102
- await TxProver.buildCircuitProver(config),
110
+ await TxProver.buildCircuitProver(config, telemetry),
103
111
  config.proverAgentConcurrency,
104
112
  config.proverAgentPollInterval,
105
113
  )
106
114
  : undefined;
107
115
 
108
- const prover = new TxProver(config, worldStateSynchronizer, vks, agent, initialHeader);
116
+ const prover = new TxProver(config, worldStateSynchronizer, vks, telemetry, agent, initialHeader);
109
117
  await prover.start();
110
118
  return prover;
111
119
  }
112
120
 
113
- private static async buildCircuitProver(config: ProverClientConfig): Promise<ServerCircuitProver> {
121
+ private static async buildCircuitProver(
122
+ config: ProverClientConfig,
123
+ telemetry: TelemetryClient,
124
+ ): Promise<ServerCircuitProver> {
114
125
  if (config.realProofs) {
115
- return await BBNativeRollupProver.new(config);
126
+ return await BBNativeRollupProver.new(config, telemetry);
116
127
  }
117
128
 
118
129
  const simulationProvider = config.acvmBinaryPath
119
130
  ? new NativeACVMSimulator(config.acvmWorkingDirectory, config.acvmBinaryPath)
120
131
  : undefined;
121
132
 
122
- return new TestCircuitProver(simulationProvider);
133
+ return new TestCircuitProver(telemetry, simulationProvider);
123
134
  }
124
135
 
125
136
  /**