@aztec/prover-client 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a

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 (72) hide show
  1. package/dest/config.d.ts +1 -1
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +4 -0
  4. package/dest/light/lightweight_checkpoint_builder.d.ts +1 -1
  5. package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -1
  6. package/dest/light/lightweight_checkpoint_builder.js +12 -4
  7. package/dest/mocks/test_context.d.ts +3 -1
  8. package/dest/mocks/test_context.d.ts.map +1 -1
  9. package/dest/mocks/test_context.js +3 -1
  10. package/dest/orchestrator/block-building-helpers.d.ts +1 -1
  11. package/dest/orchestrator/checkpoint-proving-state.d.ts +8 -1
  12. package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -1
  13. package/dest/orchestrator/checkpoint-proving-state.js +10 -1
  14. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts +107 -0
  15. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts.map +1 -0
  16. package/dest/orchestrator/checkpoint-sub-tree-orchestrator.js +151 -0
  17. package/dest/orchestrator/epoch-proving-context.d.ts +51 -0
  18. package/dest/orchestrator/epoch-proving-context.d.ts.map +1 -0
  19. package/dest/orchestrator/epoch-proving-context.js +81 -0
  20. package/dest/orchestrator/index.d.ts +4 -1
  21. package/dest/orchestrator/index.d.ts.map +1 -1
  22. package/dest/orchestrator/index.js +3 -0
  23. package/dest/orchestrator/orchestrator.d.ts +14 -26
  24. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  25. package/dest/orchestrator/orchestrator.js +32 -169
  26. package/dest/orchestrator/proving-scheduler.d.ts +72 -0
  27. package/dest/orchestrator/proving-scheduler.d.ts.map +1 -0
  28. package/dest/orchestrator/proving-scheduler.js +117 -0
  29. package/dest/orchestrator/top-tree-orchestrator.d.ts +83 -0
  30. package/dest/orchestrator/top-tree-orchestrator.d.ts.map +1 -0
  31. package/dest/orchestrator/top-tree-orchestrator.js +182 -0
  32. package/dest/orchestrator/top-tree-proving-scheduler.d.ts +62 -0
  33. package/dest/orchestrator/top-tree-proving-scheduler.d.ts.map +1 -0
  34. package/dest/orchestrator/top-tree-proving-scheduler.js +73 -0
  35. package/dest/orchestrator/top-tree-proving-state.d.ts +61 -0
  36. package/dest/orchestrator/top-tree-proving-state.d.ts.map +1 -0
  37. package/dest/orchestrator/top-tree-proving-state.js +185 -0
  38. package/dest/prover-client/prover-client.d.ts +61 -2
  39. package/dest/prover-client/prover-client.d.ts.map +1 -1
  40. package/dest/prover-client/prover-client.js +49 -1
  41. package/dest/proving_broker/config.d.ts +8 -72
  42. package/dest/proving_broker/config.d.ts.map +1 -1
  43. package/dest/proving_broker/config.js +2 -2
  44. package/dest/proving_broker/index.d.ts +2 -1
  45. package/dest/proving_broker/index.d.ts.map +1 -1
  46. package/dest/proving_broker/index.js +1 -0
  47. package/dest/proving_broker/proving_broker.d.ts +2 -2
  48. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  49. package/dest/proving_broker/proving_broker.js +5 -3
  50. package/dest/proving_broker/proving_broker_database/persisted.js +2 -2
  51. package/dest/proving_broker/rpc.d.ts +1 -1
  52. package/dest/proving_broker/rpc.d.ts.map +1 -1
  53. package/dest/proving_broker/rpc.js +64 -9
  54. package/package.json +17 -17
  55. package/src/config.ts +5 -0
  56. package/src/light/lightweight_checkpoint_builder.ts +14 -6
  57. package/src/mocks/test_context.ts +3 -1
  58. package/src/orchestrator/checkpoint-proving-state.ts +14 -1
  59. package/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +271 -0
  60. package/src/orchestrator/epoch-proving-context.ts +101 -0
  61. package/src/orchestrator/index.ts +8 -0
  62. package/src/orchestrator/orchestrator.ts +46 -225
  63. package/src/orchestrator/proving-scheduler.ts +156 -0
  64. package/src/orchestrator/top-tree-orchestrator.ts +314 -0
  65. package/src/orchestrator/top-tree-proving-scheduler.ts +154 -0
  66. package/src/orchestrator/top-tree-proving-state.ts +220 -0
  67. package/src/prover-client/prover-client.ts +125 -1
  68. package/src/proving_broker/config.ts +2 -1
  69. package/src/proving_broker/index.ts +1 -0
  70. package/src/proving_broker/proving_broker.ts +5 -3
  71. package/src/proving_broker/proving_broker_database/persisted.ts +2 -2
  72. package/src/proving_broker/rpc.ts +24 -21
@@ -3,18 +3,13 @@ import {
3
3
  L1_TO_L2_MSG_SUBTREE_HEIGHT,
4
4
  L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH,
5
5
  NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
6
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
7
6
  NUM_BASE_PARITY_PER_ROOT_PARITY,
8
7
  } from '@aztec/constants';
9
8
  import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
10
- import { padArrayEnd } from '@aztec/foundation/collection';
11
9
  import { Fr } from '@aztec/foundation/curves/bn254';
12
- import { AbortError } from '@aztec/foundation/error';
13
- import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
10
+ import type { LoggerBindings } from '@aztec/foundation/log';
14
11
  import { promiseWithResolvers } from '@aztec/foundation/promise';
15
- import { SerialQueue } from '@aztec/foundation/queue';
16
12
  import { assertLength } from '@aztec/foundation/serialize';
17
- import { sleep } from '@aztec/foundation/sleep';
18
13
  import { pushTestData } from '@aztec/foundation/testing';
19
14
  import { elapsed } from '@aztec/foundation/timer';
20
15
  import type { TreeNodeLocation } from '@aztec/foundation/trees';
@@ -27,6 +22,7 @@ import type {
27
22
  ReadonlyWorldStateAccess,
28
23
  ServerCircuitProver,
29
24
  } from '@aztec/stdlib/interfaces/server';
25
+ import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging';
30
26
  import type { Proof } from '@aztec/stdlib/proofs';
31
27
  import {
32
28
  type BaseRollupHints,
@@ -71,6 +67,7 @@ import type { BlockProvingState } from './block-proving-state.js';
71
67
  import type { CheckpointProvingState } from './checkpoint-proving-state.js';
72
68
  import { EpochProvingState, type ProvingResult, type TreeSnapshots } from './epoch-proving-state.js';
73
69
  import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js';
70
+ import { TopTreeProvingScheduler } from './top-tree-proving-scheduler.js';
74
71
  import { TxProvingState } from './tx-proving-state.js';
75
72
 
76
73
  /**
@@ -87,29 +84,25 @@ import { TxProvingState } from './tx-proving-state.js';
87
84
  /**
88
85
  * The orchestrator, managing the flow of recursive proving operations required to build the rollup proof tree.
89
86
  */
90
- export class ProvingOrchestrator implements EpochProver {
91
- private provingState: EpochProvingState | undefined = undefined;
92
- private pendingProvingJobs: AbortController[] = [];
87
+ export class ProvingOrchestrator extends TopTreeProvingScheduler implements EpochProver {
88
+ protected provingState: EpochProvingState | undefined = undefined;
93
89
 
94
- private provingPromise: Promise<ProvingResult> | undefined = undefined;
90
+ protected provingPromise: Promise<ProvingResult> | undefined = undefined;
95
91
  private metrics: ProvingOrchestratorMetrics;
96
- // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
92
+
97
93
  private dbs: Map<BlockNumber, MerkleTreeWriteOperations> = new Map();
98
- private logger: Logger;
99
- private deferredJobQueue = new SerialQueue();
100
94
 
101
95
  constructor(
102
96
  private dbProvider: ReadonlyWorldStateAccess & ForkMerkleTreeOperations,
103
- private prover: ServerCircuitProver,
97
+ prover: ServerCircuitProver,
104
98
  private readonly proverId: EthAddress,
105
99
  private readonly cancelJobsOnStop: boolean = false,
106
- private readonly enqueueConcurrency: number,
100
+ enqueueConcurrency: number,
107
101
  telemetryClient: TelemetryClient = getTelemetryClient(),
108
102
  bindings?: LoggerBindings,
109
103
  ) {
110
- this.logger = createLogger('prover-client:orchestrator', bindings);
104
+ super(prover, enqueueConcurrency, 'prover-client:orchestrator', bindings);
111
105
  this.metrics = new ProvingOrchestratorMetrics(telemetryClient, 'ProvingOrchestrator');
112
- this.deferredJobQueue.start(this.enqueueConcurrency);
113
106
  }
114
107
 
115
108
  get tracer(): Tracer {
@@ -124,11 +117,24 @@ export class ProvingOrchestrator implements EpochProver {
124
117
  return this.dbs.size;
125
118
  }
126
119
 
127
- public async stop(): Promise<void> {
128
- // Grab the old queue before cancel() replaces it, so we can await its draining.
129
- const oldQueue = this.deferredJobQueue;
120
+ protected override cancelInternal(): void {
130
121
  this.cancel();
131
- await oldQueue.cancel();
122
+ }
123
+
124
+ protected override wrapCircuitCall<T>(
125
+ circuitName: string,
126
+ fn: (signal: AbortSignal) => Promise<T>,
127
+ ): (signal: AbortSignal) => Promise<T> {
128
+ return wrapCallbackInSpan(
129
+ this.tracer,
130
+ `ProvingOrchestrator.prover.${circuitName}`,
131
+ { [Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName as CircuitName },
132
+ fn,
133
+ );
134
+ }
135
+
136
+ protected override onRootRollupComplete(state: EpochProvingState) {
137
+ state.resolve({ status: 'success' });
132
138
  }
133
139
 
134
140
  public startNewEpoch(
@@ -521,16 +527,7 @@ export class ProvingOrchestrator implements EpochProver {
521
527
  * If cancelJobsOnStop is false (default), jobs remain in the broker queue and can be reused on restart/reorg.
522
528
  */
523
529
  public cancel() {
524
- void this.deferredJobQueue.cancel();
525
- // Recreate the queue so it can accept jobs for subsequent epochs.
526
- this.deferredJobQueue = new SerialQueue();
527
- this.deferredJobQueue.start(this.enqueueConcurrency);
528
-
529
- if (this.cancelJobsOnStop) {
530
- for (const controller of this.pendingProvingJobs) {
531
- controller.abort();
532
- }
533
- }
530
+ this.resetSchedulerState(this.cancelJobsOnStop);
534
531
 
535
532
  this.provingState?.cancel();
536
533
 
@@ -577,79 +574,7 @@ export class ProvingOrchestrator implements EpochProver {
577
574
  return epochProofResult;
578
575
  }
579
576
 
580
- /**
581
- * Enqueue a job to be scheduled
582
- * @param provingState - The proving state object being operated on
583
- * @param jobType - The type of job to be queued
584
- * @param job - The actual job, returns a promise notifying of the job's completion
585
- */
586
- private deferredProving<T>(
587
- provingState: EpochProvingState | CheckpointProvingState | BlockProvingState,
588
- request: (signal: AbortSignal) => Promise<T>,
589
- callback: (result: T) => void | Promise<void>,
590
- ) {
591
- if (!provingState.verifyState()) {
592
- this.logger.debug(`Not enqueuing job, state no longer valid`);
593
- return;
594
- }
595
-
596
- const controller = new AbortController();
597
- this.pendingProvingJobs.push(controller);
598
-
599
- // We use a 'safeJob'. We don't want promise rejections in the proving pool, we want to capture the error here
600
- // and reject the proving job whilst keeping the event loop free of rejections
601
- const safeJob = async () => {
602
- try {
603
- // there's a delay between enqueueing this job and it actually running
604
- if (controller.signal.aborted) {
605
- return;
606
- }
607
-
608
- const result = await request(controller.signal);
609
- if (!provingState.verifyState()) {
610
- this.logger.debug(`State no longer valid, discarding result`);
611
- return;
612
- }
613
-
614
- // we could have been cancelled whilst waiting for the result
615
- // and the prover ignored the signal. Drop the result in that case
616
- if (controller.signal.aborted) {
617
- return;
618
- }
619
-
620
- await callback(result);
621
- } catch (err) {
622
- if (err instanceof AbortError) {
623
- // operation was cancelled, probably because the block was cancelled
624
- // drop this result
625
- return;
626
- }
627
-
628
- this.logger.error(`Error thrown when proving job`, err);
629
- provingState!.reject(`${err}`);
630
- } finally {
631
- const index = this.pendingProvingJobs.indexOf(controller);
632
- if (index > -1) {
633
- this.pendingProvingJobs.splice(index, 1);
634
- }
635
- }
636
- };
637
-
638
- void this.deferredJobQueue.put(async () => {
639
- void safeJob();
640
- // we yield here to the macro task queue such to give Nodejs a chance to run other operatoins in between enqueues
641
- await sleep(0);
642
- });
643
- }
644
-
645
577
  private async updateL1ToL2MessageTree(l1ToL2Messages: Fr[], db: MerkleTreeWriteOperations) {
646
- const l1ToL2MessagesPadded = padArrayEnd<Fr, number>(
647
- l1ToL2Messages,
648
- Fr.ZERO,
649
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
650
- 'Too many L1 to L2 messages',
651
- );
652
-
653
578
  const lastL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
654
579
  const lastL1ToL2MessageSubtreeRootSiblingPath = assertLength(
655
580
  await getSubtreeSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_SUBTREE_HEIGHT, db),
@@ -657,7 +582,7 @@ export class ProvingOrchestrator implements EpochProver {
657
582
  );
658
583
 
659
584
  // Update the local trees to include the new l1 to l2 messages
660
- await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
585
+ await appendL1ToL2MessagesToTree(db, l1ToL2Messages);
661
586
 
662
587
  const newL1ToL2MessageTreeSnapshot = await getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, db);
663
588
  const newL1ToL2MessageSubtreeRootSiblingPath = assertLength(
@@ -764,7 +689,7 @@ export class ProvingOrchestrator implements EpochProver {
764
689
 
765
690
  // Enqueues the public chonk verifier circuit for a given transaction index, or reuses the one already enqueued.
766
691
  // Once completed, will enqueue the the public tx base rollup.
767
- private getOrEnqueueChonkVerifier(provingState: BlockProvingState, txIndex: number) {
692
+ protected getOrEnqueueChonkVerifier(provingState: BlockProvingState, txIndex: number) {
768
693
  if (!provingState.verifyState()) {
769
694
  this.logger.debug('Not running chonk verifier circuit, state invalid');
770
695
  return;
@@ -772,7 +697,6 @@ export class ProvingOrchestrator implements EpochProver {
772
697
 
773
698
  const txProvingState = provingState.getTxProvingState(txIndex);
774
699
  const txHash = txProvingState.processedTx.hash.toString();
775
- NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH;
776
700
  const handleResult = (
777
701
  result: PublicInputsAndRecursiveProof<
778
702
  PublicChonkVerifierPublicInputs,
@@ -897,7 +821,11 @@ export class ProvingOrchestrator implements EpochProver {
897
821
  },
898
822
  ),
899
823
  async result => {
900
- this.logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}`);
824
+ this.logger.debug(`Completed ${rollupType} proof for block ${provingState.blockNumber}`, {
825
+ blockNumber: provingState.blockNumber,
826
+ checkpointIndex: provingState.parentCheckpoint.index,
827
+ ...result.inputs.toInspect(),
828
+ });
901
829
 
902
830
  const leafLocation = provingState.setBlockRootRollupProof(result);
903
831
  const checkpointProvingState = provingState.parentCheckpoint;
@@ -1016,6 +944,11 @@ export class ProvingOrchestrator implements EpochProver {
1016
944
  signal => this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber),
1017
945
  ),
1018
946
  async result => {
947
+ this.logger.debug(`Completed block merge rollup proof for checkpoint ${provingState.index}`, {
948
+ checkpointIndex: provingState.index,
949
+ mergeLocation: location,
950
+ ...result.inputs.toInspect(),
951
+ });
1019
952
  provingState.setBlockMergeRollupProof(location, result);
1020
953
  await this.checkAndEnqueueNextBlockMergeRollup(provingState, location);
1021
954
  },
@@ -1068,7 +1001,10 @@ export class ProvingOrchestrator implements EpochProver {
1068
1001
  return;
1069
1002
  }
1070
1003
 
1071
- this.logger.debug(`Completed ${rollupType} proof for checkpoint ${provingState.index}.`);
1004
+ this.logger.debug(`Completed ${rollupType} proof for checkpoint ${provingState.index}`, {
1005
+ checkpointIndex: provingState.index,
1006
+ ...result.inputs.toInspect(),
1007
+ });
1072
1008
 
1073
1009
  const leafLocation = provingState.setCheckpointRootRollupProof(result);
1074
1010
  const epochProvingState = provingState.parentEpoch;
@@ -1082,99 +1018,6 @@ export class ProvingOrchestrator implements EpochProver {
1082
1018
  );
1083
1019
  }
1084
1020
 
1085
- private enqueueCheckpointMergeRollup(provingState: EpochProvingState, location: TreeNodeLocation) {
1086
- if (!provingState.verifyState()) {
1087
- this.logger.debug('Not running checkpoint merge rollup. State no longer valid.');
1088
- return;
1089
- }
1090
-
1091
- if (!provingState.tryStartProvingCheckpointMerge(location)) {
1092
- this.logger.debug('Checkpoint merge rollup already started.');
1093
- return;
1094
- }
1095
-
1096
- const inputs = provingState.getCheckpointMergeRollupInputs(location);
1097
-
1098
- this.deferredProving(
1099
- provingState,
1100
- wrapCallbackInSpan(
1101
- this.tracer,
1102
- 'ProvingOrchestrator.prover.getCheckpointMergeRollupProof',
1103
- {
1104
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-merge' satisfies CircuitName,
1105
- },
1106
- signal => this.prover.getCheckpointMergeRollupProof(inputs, signal, provingState.epochNumber),
1107
- ),
1108
- result => {
1109
- this.logger.debug('Completed proof for checkpoint merge rollup.');
1110
- provingState.setCheckpointMergeRollupProof(location, result);
1111
- this.checkAndEnqueueNextCheckpointMergeRollup(provingState, location);
1112
- },
1113
- );
1114
- }
1115
-
1116
- private enqueueEpochPadding(provingState: EpochProvingState) {
1117
- if (!provingState.verifyState()) {
1118
- this.logger.debug('Not running epoch padding. State no longer valid.');
1119
- return;
1120
- }
1121
-
1122
- if (!provingState.tryStartProvingPaddingCheckpoint()) {
1123
- this.logger.debug('Padding checkpoint already started.');
1124
- return;
1125
- }
1126
-
1127
- this.logger.debug('Padding epoch proof with a padding block root proof.');
1128
-
1129
- const inputs = provingState.getPaddingCheckpointInputs();
1130
-
1131
- this.deferredProving(
1132
- provingState,
1133
- wrapCallbackInSpan(
1134
- this.tracer,
1135
- 'ProvingOrchestrator.prover.getCheckpointPaddingRollupProof',
1136
- {
1137
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-checkpoint-padding' satisfies CircuitName,
1138
- },
1139
- signal => this.prover.getCheckpointPaddingRollupProof(inputs, signal, provingState.epochNumber),
1140
- ),
1141
- result => {
1142
- this.logger.debug('Completed proof for padding checkpoint.');
1143
- provingState.setCheckpointPaddingProof(result);
1144
- this.checkAndEnqueueRootRollup(provingState);
1145
- },
1146
- );
1147
- }
1148
-
1149
- // Executes the root rollup circuit
1150
- private enqueueRootRollup(provingState: EpochProvingState) {
1151
- if (!provingState.verifyState()) {
1152
- this.logger.debug('Not running root rollup, state no longer valid');
1153
- return;
1154
- }
1155
-
1156
- this.logger.debug(`Preparing root rollup`);
1157
-
1158
- const inputs = provingState.getRootRollupInputs();
1159
-
1160
- this.deferredProving(
1161
- provingState,
1162
- wrapCallbackInSpan(
1163
- this.tracer,
1164
- 'ProvingOrchestrator.prover.getRootRollupProof',
1165
- {
1166
- [Attributes.PROTOCOL_CIRCUIT_NAME]: 'rollup-root' satisfies CircuitName,
1167
- },
1168
- signal => this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber),
1169
- ),
1170
- result => {
1171
- this.logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`);
1172
- provingState.setRootRollupProof(result);
1173
- provingState.resolve({ status: 'success' });
1174
- },
1175
- );
1176
- }
1177
-
1178
1021
  private checkAndEnqueueNextMergeRollup(provingState: BlockProvingState, currentLocation: TreeNodeLocation) {
1179
1022
  if (!provingState.isReadyForMergeRollup(currentLocation)) {
1180
1023
  return;
@@ -1213,7 +1056,7 @@ export class ProvingOrchestrator implements EpochProver {
1213
1056
  }
1214
1057
  }
1215
1058
 
1216
- private async checkAndEnqueueCheckpointRootRollup(provingState: CheckpointProvingState) {
1059
+ protected async checkAndEnqueueCheckpointRootRollup(provingState: CheckpointProvingState) {
1217
1060
  if (!provingState.isReadyForCheckpointRoot()) {
1218
1061
  return;
1219
1062
  }
@@ -1221,28 +1064,6 @@ export class ProvingOrchestrator implements EpochProver {
1221
1064
  await this.enqueueCheckpointRootRollup(provingState);
1222
1065
  }
1223
1066
 
1224
- private checkAndEnqueueNextCheckpointMergeRollup(provingState: EpochProvingState, currentLocation: TreeNodeLocation) {
1225
- if (!provingState.isReadyForCheckpointMerge(currentLocation)) {
1226
- return;
1227
- }
1228
-
1229
- const parentLocation = provingState.getParentLocation(currentLocation);
1230
- if (parentLocation.level === 0) {
1231
- this.checkAndEnqueueRootRollup(provingState);
1232
- } else {
1233
- this.enqueueCheckpointMergeRollup(provingState, parentLocation);
1234
- }
1235
- }
1236
-
1237
- private checkAndEnqueueRootRollup(provingState: EpochProvingState) {
1238
- if (!provingState.isReadyForRootRollup()) {
1239
- this.logger.debug('Not ready for root rollup');
1240
- return;
1241
- }
1242
-
1243
- this.enqueueRootRollup(provingState);
1244
- }
1245
-
1246
1067
  /**
1247
1068
  * Executes the VM circuit for a public function, will enqueue the corresponding kernel if the
1248
1069
  * previous kernel is ready
@@ -1276,7 +1097,7 @@ export class ProvingOrchestrator implements EpochProver {
1276
1097
  });
1277
1098
  }
1278
1099
 
1279
- private checkAndEnqueueBaseRollup(provingState: BlockProvingState, txIndex: number) {
1100
+ protected checkAndEnqueueBaseRollup(provingState: BlockProvingState, txIndex: number) {
1280
1101
  const txProvingState = provingState.getTxProvingState(txIndex);
1281
1102
  if (!txProvingState.ready()) {
1282
1103
  return;
@@ -0,0 +1,156 @@
1
+ import { AbortError } from '@aztec/foundation/error';
2
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
+ import { SerialQueue } from '@aztec/foundation/queue';
4
+ import { sleep } from '@aztec/foundation/sleep';
5
+
6
+ /**
7
+ * Minimal surface a deferred-proving state must expose. Both `EpochProvingState` /
8
+ * `CheckpointProvingState` / `BlockProvingState` (used by `ProvingOrchestrator`) and
9
+ * `TopTreeProvingState` (used by `TopTreeOrchestrator`) satisfy it.
10
+ */
11
+ export interface ProvingStateLike {
12
+ /** Returns false once the state has been cancelled or otherwise invalidated. */
13
+ verifyState(): boolean;
14
+ /** Surfaces a proving error to the state's owner. */
15
+ reject(reason: string): void;
16
+ }
17
+
18
+ /**
19
+ * Common scheduling infrastructure shared by every orchestrator that drives broker
20
+ * proving jobs:
21
+ *
22
+ * - One `SerialQueue` (`deferredJobQueue`) acting as the enqueue-throttle.
23
+ * - A list of `AbortController`s (`pendingProvingJobs`) so a `cancel()` can abort
24
+ * in-flight broker jobs when needed.
25
+ * - A `deferredProving<T>(state, request, callback, isCancelled?)` method that wraps
26
+ * a broker request in the standard "submit, drop result if state invalidated, push
27
+ * errors to state.reject" envelope.
28
+ *
29
+ * Subclasses own their own concrete proving state and define `cancelInternal()` for
30
+ * the rest of the cleanup work (closing world-state forks, marking sub-trees
31
+ * cancelled, etc.). `stop()` lives on the base class and follows the standard pattern
32
+ * of grabbing the old queue, calling `cancelInternal()` (which recreates the queue),
33
+ * and awaiting the old queue's drain.
34
+ */
35
+ export abstract class ProvingScheduler {
36
+ protected pendingProvingJobs: AbortController[] = [];
37
+ protected logger: Logger;
38
+ private deferredJobQueue: SerialQueue;
39
+
40
+ constructor(
41
+ private readonly enqueueConcurrency: number,
42
+ loggerName = 'prover-client:proving-scheduler',
43
+ bindings?: LoggerBindings,
44
+ ) {
45
+ this.logger = createLogger(loggerName, bindings);
46
+ this.deferredJobQueue = new SerialQueue();
47
+ this.deferredJobQueue.start(this.enqueueConcurrency);
48
+ }
49
+
50
+ /** Number of broker jobs currently in flight. */
51
+ public getNumPendingProvingJobs(): number {
52
+ return this.pendingProvingJobs.length;
53
+ }
54
+
55
+ /**
56
+ * Drains the deferred-job queue, recreates it (so the subclass can be reused), and
57
+ * optionally aborts every in-flight broker job. Aborting is the right choice on
58
+ * reorg-driven cancel (where the in-flight inputs are no longer valid) and the
59
+ * wrong choice on shutdown (where leaving jobs in the broker queue lets a restart
60
+ * pick them up).
61
+ */
62
+ protected resetSchedulerState(abortJobs: boolean): void {
63
+ void this.deferredJobQueue.cancel();
64
+ this.deferredJobQueue = new SerialQueue();
65
+ this.deferredJobQueue.start(this.enqueueConcurrency);
66
+ if (abortJobs) {
67
+ for (const controller of this.pendingProvingJobs) {
68
+ controller.abort();
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Subclass-defined cancellation. Implementations call `resetSchedulerState(...)`
75
+ * and then do their own cleanup (close world-state forks, propagate cancel into
76
+ * the proving state, etc.).
77
+ */
78
+ protected abstract cancelInternal(): void;
79
+
80
+ /**
81
+ * Standard stop: grab the old queue, cancel (which recreates the queue), then
82
+ * await the old queue's drain so any final job tear-down has unwound before we
83
+ * return.
84
+ */
85
+ public async stop(): Promise<void> {
86
+ const oldQueue = this.deferredJobQueue;
87
+ this.cancelInternal();
88
+ await oldQueue.cancel();
89
+ }
90
+
91
+ /**
92
+ * Submits a broker request. The returned-via-callback result is dropped if the
93
+ * state has become invalid (re-org, cancellation) by the time it lands. Errors
94
+ * are routed to `state.reject` unless they are abort-driven or the state is
95
+ * already invalid (in which case they're a stale echo of the cancel).
96
+ *
97
+ * @param state - Object exposing `verifyState()` and `reject()`.
98
+ * @param request - The broker call. Receives the controller's signal.
99
+ * @param callback - Runs on success, after `verifyState()` is checked.
100
+ * @param isCancelled - Optional extra cancellation predicate (e.g. a `cancelled`
101
+ * flag the subclass maintains independently of the state). Defaults to never.
102
+ */
103
+ protected deferredProving<S extends ProvingStateLike, T>(
104
+ state: S,
105
+ request: (signal: AbortSignal) => Promise<T>,
106
+ callback: (result: T) => void | Promise<void>,
107
+ isCancelled: () => boolean = () => false,
108
+ ): void {
109
+ if (!state.verifyState()) {
110
+ this.logger.debug(`Not enqueuing job, state no longer valid`);
111
+ return;
112
+ }
113
+
114
+ const controller = new AbortController();
115
+ this.pendingProvingJobs.push(controller);
116
+
117
+ // We use a 'safeJob'. We don't want promise rejections in the proving pool — we
118
+ // want to capture the error here and reject the proving state while keeping the
119
+ // event loop free of rejections.
120
+ const safeJob = async () => {
121
+ try {
122
+ if (controller.signal.aborted) {
123
+ return;
124
+ }
125
+ const result = await request(controller.signal);
126
+ if (controller.signal.aborted || !state.verifyState() || isCancelled()) {
127
+ this.logger.debug(`State no longer valid, discarding result`);
128
+ return;
129
+ }
130
+ await callback(result);
131
+ } catch (err) {
132
+ if (err instanceof AbortError || isCancelled()) {
133
+ return;
134
+ }
135
+ if (!state.verifyState()) {
136
+ this.logger.debug(`State no longer valid, discarding error from proving job`, err);
137
+ return;
138
+ }
139
+ this.logger.error(`Error thrown when proving job`, err);
140
+ state.reject(`${err}`);
141
+ } finally {
142
+ const idx = this.pendingProvingJobs.indexOf(controller);
143
+ if (idx > -1) {
144
+ this.pendingProvingJobs.splice(idx, 1);
145
+ }
146
+ }
147
+ };
148
+
149
+ void this.deferredJobQueue.put(async () => {
150
+ void safeJob();
151
+ // Yield to the macrotask queue so Node has a chance to interleave other work
152
+ // between enqueues.
153
+ await sleep(0);
154
+ });
155
+ }
156
+ }