@aztec/prover-client 0.63.1 → 0.65.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.
@@ -13,10 +13,8 @@ import { type Fr } from '@aztec/foundation/fields';
13
13
  import { type DebugLogger } from '@aztec/foundation/log';
14
14
  import { openTmpStore } from '@aztec/kv-store/utils';
15
15
  import {
16
- type PublicExecutionResult,
17
- PublicExecutionResultBuilder,
18
- type PublicExecutor,
19
16
  PublicProcessor,
17
+ PublicTxSimulator,
20
18
  type SimulationProvider,
21
19
  WASMSimulator,
22
20
  type WorldStateDB,
@@ -25,10 +23,12 @@ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
25
23
  import { MerkleTrees } from '@aztec/world-state';
26
24
  import { NativeWorldStateService } from '@aztec/world-state/native';
27
25
 
26
+ import { jest } from '@jest/globals';
28
27
  import * as fs from 'fs/promises';
29
28
  import { type MockProxy, mock } from 'jest-mock-extended';
30
29
 
31
30
  import { TestCircuitProver } from '../../../bb-prover/src/test/test_circuit_prover.js';
31
+ import { AvmFinalizedCallResult } from '../../../simulator/src/avm/avm_contract_call_result.js';
32
32
  import { type AvmPersistableStateManager } from '../../../simulator/src/avm/journal/journal.js';
33
33
  import { ProvingOrchestrator } from '../orchestrator/index.js';
34
34
  import { MemoryProvingQueue } from '../prover-agent/memory-proving-queue.js';
@@ -37,7 +37,7 @@ import { getEnvironmentConfig, getSimulationProvider, makeGlobals } from './fixt
37
37
 
38
38
  export class TestContext {
39
39
  constructor(
40
- public publicExecutor: MockProxy<PublicExecutor>,
40
+ public publicTxSimulator: PublicTxSimulator,
41
41
  public worldStateDB: MockProxy<WorldStateDB>,
42
42
  public publicProcessor: PublicProcessor,
43
43
  public simulationProvider: SimulationProvider,
@@ -66,7 +66,6 @@ export class TestContext {
66
66
  const directoriesToCleanup: string[] = [];
67
67
  const globalVariables = makeGlobals(blockNumber);
68
68
 
69
- const publicExecutor = mock<PublicExecutor>();
70
69
  const worldStateDB = mock<WorldStateDB>();
71
70
  const telemetry = new NoopTelemetryClient();
72
71
 
@@ -83,13 +82,15 @@ export class TestContext {
83
82
  publicDb = await ws.getLatest();
84
83
  proverDb = await ws.getLatest();
85
84
  }
85
+ worldStateDB.getMerkleInterface.mockReturnValue(publicDb);
86
86
 
87
- const processor = PublicProcessor.create(
87
+ const publicTxSimulator = new PublicTxSimulator(publicDb, worldStateDB, telemetry, globalVariables);
88
+ const processor = new PublicProcessor(
88
89
  publicDb,
89
- publicExecutor,
90
90
  globalVariables,
91
91
  Header.empty(),
92
92
  worldStateDB,
93
+ publicTxSimulator,
93
94
  telemetry,
94
95
  );
95
96
 
@@ -124,7 +125,7 @@ export class TestContext {
124
125
  agent.start(queue);
125
126
 
126
127
  return new this(
127
- publicExecutor,
128
+ publicTxSimulator,
128
129
  worldStateDB,
129
130
  processor,
130
131
  simulationProvider,
@@ -154,25 +155,24 @@ export class TestContext {
154
155
  ) {
155
156
  const defaultExecutorImplementation = (
156
157
  _stateManager: AvmPersistableStateManager,
157
- execution: PublicExecutionRequest,
158
- _globalVariables: GlobalVariables,
158
+ executionRequest: PublicExecutionRequest,
159
159
  allocatedGas: Gas,
160
- _transactionFee?: Fr,
160
+ _transactionFee: Fr,
161
+ _fnName: string,
161
162
  ) => {
162
163
  for (const tx of txs) {
163
164
  const allCalls = tx.publicTeardownFunctionCall.isEmpty()
164
165
  ? tx.enqueuedPublicFunctionCalls
165
166
  : [...tx.enqueuedPublicFunctionCalls, tx.publicTeardownFunctionCall];
166
167
  for (const request of allCalls) {
167
- if (execution.callContext.equals(request.callContext)) {
168
- const result = PublicExecutionResultBuilder.empty().build({
169
- endGasLeft: allocatedGas,
170
- });
171
- return Promise.resolve(result);
168
+ if (executionRequest.callContext.equals(request.callContext)) {
169
+ return Promise.resolve(
170
+ new AvmFinalizedCallResult(/*reverted=*/ false, /*output=*/ [], /*gasLeft=*/ allocatedGas),
171
+ );
172
172
  }
173
173
  }
174
174
  }
175
- throw new Error(`Unexpected execution request: ${execution}`);
175
+ throw new Error(`Unexpected execution request: ${executionRequest}`);
176
176
  };
177
177
  return await this.processPublicFunctionsWithMockExecutorImplementation(
178
178
  txs,
@@ -183,21 +183,36 @@ export class TestContext {
183
183
  );
184
184
  }
185
185
 
186
- public async processPublicFunctionsWithMockExecutorImplementation(
186
+ private async processPublicFunctionsWithMockExecutorImplementation(
187
187
  txs: Tx[],
188
188
  maxTransactions: number,
189
189
  txHandler?: ProcessedTxHandler,
190
190
  txValidator?: TxValidator<ProcessedTx>,
191
191
  executorMock?: (
192
192
  stateManager: AvmPersistableStateManager,
193
- execution: PublicExecutionRequest,
194
- globalVariables: GlobalVariables,
193
+ executionRequest: PublicExecutionRequest,
195
194
  allocatedGas: Gas,
196
- transactionFee?: Fr,
197
- ) => Promise<PublicExecutionResult>,
195
+ transactionFee: Fr,
196
+ fnName: string,
197
+ ) => Promise<AvmFinalizedCallResult>,
198
198
  ) {
199
+ // Mock the internal private function. Borrowed from https://stackoverflow.com/a/71033167
200
+ const simulateInternal: jest.SpiedFunction<
201
+ (
202
+ stateManager: AvmPersistableStateManager,
203
+ executionResult: any,
204
+ allocatedGas: Gas,
205
+ transactionFee: any,
206
+ fnName: any,
207
+ ) => Promise<AvmFinalizedCallResult>
208
+ > = jest.spyOn(
209
+ this.publicTxSimulator as unknown as {
210
+ simulateEnqueuedCallInternal: PublicTxSimulator['simulateEnqueuedCallInternal'];
211
+ },
212
+ 'simulateEnqueuedCallInternal',
213
+ );
199
214
  if (executorMock) {
200
- this.publicExecutor.simulate.mockImplementation(executorMock);
215
+ simulateInternal.mockImplementation(executorMock);
201
216
  }
202
217
  return await this.publicProcessor.process(txs, maxTransactions, txHandler, txValidator);
203
218
  }
@@ -10,7 +10,6 @@ import {
10
10
  ARCHIVE_HEIGHT,
11
11
  AppendOnlyTreeSnapshot,
12
12
  type BaseOrMergeRollupPublicInputs,
13
- BaseRollupHints,
14
13
  BlockMergeRollupInputs,
15
14
  type BlockRootOrBlockMergePublicInputs,
16
15
  ConstantRollupData,
@@ -32,25 +31,25 @@ import {
32
31
  NULLIFIER_TREE_HEIGHT,
33
32
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
34
33
  NullifierLeafPreimage,
35
- PUBLIC_DATA_SUBTREE_HEIGHT,
36
- PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH,
37
34
  PUBLIC_DATA_TREE_HEIGHT,
38
35
  type ParityPublicInputs,
39
36
  PartialStateReference,
40
37
  PreviousRollupBlockData,
41
38
  PreviousRollupData,
39
+ PrivateBaseRollupHints,
40
+ PrivateBaseStateDiffHints,
41
+ PublicBaseRollupHints,
42
+ PublicBaseStateDiffHints,
42
43
  PublicDataHint,
43
44
  PublicDataTreeLeaf,
44
- type PublicDataTreeLeafPreimage,
45
- PublicDataWrite,
45
+ PublicDataTreeLeafPreimage,
46
46
  type RecursiveProof,
47
47
  RootRollupInputs,
48
- StateDiffHints,
49
48
  StateReference,
50
49
  VK_TREE_HEIGHT,
51
50
  type VerificationKeyAsFields,
52
51
  } from '@aztec/circuits.js';
53
- import { assertPermutation, makeTuple } from '@aztec/foundation/array';
52
+ import { makeTuple } from '@aztec/foundation/array';
54
53
  import { padArrayEnd } from '@aztec/foundation/collection';
55
54
  import { sha256Trunc } from '@aztec/foundation/crypto';
56
55
  import { type DebugLogger } from '@aztec/foundation/log';
@@ -123,6 +122,7 @@ export async function buildBaseRollupHints(
123
122
  padArrayEnd(tx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
124
123
  NULLIFIER_SUBTREE_HEIGHT,
125
124
  );
125
+
126
126
  if (nullifierWitnessLeaves === undefined) {
127
127
  throw new Error(`Could not craft nullifier batch insertion proofs`);
128
128
  }
@@ -139,45 +139,109 @@ export async function buildBaseRollupHints(
139
139
  i < nullifierSubtreeSiblingPathArray.length ? nullifierSubtreeSiblingPathArray[i] : Fr.ZERO,
140
140
  );
141
141
 
142
- const publicDataSiblingPath = txPublicDataUpdateRequestInfo.newPublicDataSubtreeSiblingPath;
143
-
144
- const stateDiffHints = StateDiffHints.from({
145
- nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
146
- i < nullifierWitnessLeaves.length
147
- ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage)
148
- : NullifierLeafPreimage.empty(),
149
- ),
150
- nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
151
- i < nullifierPredecessorMembershipWitnessesWithoutPadding.length
152
- ? nullifierPredecessorMembershipWitnessesWithoutPadding[i]
153
- : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT),
154
- ),
155
- sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])),
156
- sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]),
157
- noteHashSubtreeSiblingPath,
158
- nullifierSubtreeSiblingPath,
159
- publicDataSiblingPath,
160
- });
142
+ if (tx.avmProvingRequest) {
143
+ // Build public base rollup hints
144
+ const stateDiffHints = PublicBaseStateDiffHints.from({
145
+ nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
146
+ i < nullifierWitnessLeaves.length
147
+ ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage)
148
+ : NullifierLeafPreimage.empty(),
149
+ ),
150
+ nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
151
+ i < nullifierPredecessorMembershipWitnessesWithoutPadding.length
152
+ ? nullifierPredecessorMembershipWitnessesWithoutPadding[i]
153
+ : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT),
154
+ ),
155
+ sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])),
156
+ sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]),
157
+ noteHashSubtreeSiblingPath,
158
+ nullifierSubtreeSiblingPath,
159
+ lowPublicDataWritesPreimages: padArrayEnd(
160
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages,
161
+ PublicDataTreeLeafPreimage.empty(),
162
+ MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
163
+ ),
164
+ lowPublicDataWritesMembershipWitnesses: padArrayEnd(
165
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses,
166
+ MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT),
167
+ MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
168
+ ),
169
+ publicDataTreeSiblingPaths: padArrayEnd(
170
+ txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths,
171
+ makeTuple(PUBLIC_DATA_TREE_HEIGHT, () => Fr.ZERO),
172
+ MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
173
+ ),
174
+ });
175
+
176
+ const blockHash = tx.constants.historicalHeader.hash();
177
+ const archiveRootMembershipWitness = await getMembershipWitnessFor(
178
+ blockHash,
179
+ MerkleTreeId.ARCHIVE,
180
+ ARCHIVE_HEIGHT,
181
+ db,
182
+ );
161
183
 
162
- const blockHash = tx.constants.historicalHeader.hash();
163
- const archiveRootMembershipWitness = await getMembershipWitnessFor(
164
- blockHash,
165
- MerkleTreeId.ARCHIVE,
166
- ARCHIVE_HEIGHT,
167
- db,
168
- );
184
+ return PublicBaseRollupHints.from({
185
+ start,
186
+ stateDiffHints,
187
+ feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint,
188
+ archiveRootMembershipWitness,
189
+ constants,
190
+ });
191
+ } else {
192
+ if (
193
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses.length > 1 ||
194
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages.length > 1 ||
195
+ txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths.length > 1
196
+ ) {
197
+ throw new Error(`More than one public data write in a private only tx`);
198
+ }
199
+
200
+ const feeWriteLowLeafPreimage =
201
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages[0] || PublicDataTreeLeafPreimage.empty();
202
+ const feeWriteLowLeafMembershipWitness =
203
+ txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses[0] ||
204
+ MembershipWitness.empty<typeof PUBLIC_DATA_TREE_HEIGHT>(PUBLIC_DATA_TREE_HEIGHT);
205
+ const feeWriteSiblingPath =
206
+ txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths[0] ||
207
+ makeTuple(PUBLIC_DATA_TREE_HEIGHT, () => Fr.ZERO);
208
+
209
+ const stateDiffHints = PrivateBaseStateDiffHints.from({
210
+ nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
211
+ i < nullifierWitnessLeaves.length
212
+ ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage)
213
+ : NullifierLeafPreimage.empty(),
214
+ ),
215
+ nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i =>
216
+ i < nullifierPredecessorMembershipWitnessesWithoutPadding.length
217
+ ? nullifierPredecessorMembershipWitnessesWithoutPadding[i]
218
+ : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT),
219
+ ),
220
+ sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])),
221
+ sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]),
222
+ noteHashSubtreeSiblingPath,
223
+ nullifierSubtreeSiblingPath,
224
+ feeWriteLowLeafPreimage,
225
+ feeWriteLowLeafMembershipWitness,
226
+ feeWriteSiblingPath,
227
+ });
228
+
229
+ const blockHash = tx.constants.historicalHeader.hash();
230
+ const archiveRootMembershipWitness = await getMembershipWitnessFor(
231
+ blockHash,
232
+ MerkleTreeId.ARCHIVE,
233
+ ARCHIVE_HEIGHT,
234
+ db,
235
+ );
169
236
 
170
- return BaseRollupHints.from({
171
- start,
172
- stateDiffHints,
173
- feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint,
174
- sortedPublicDataWrites: txPublicDataUpdateRequestInfo.sortedPublicDataWrites,
175
- sortedPublicDataWritesIndexes: txPublicDataUpdateRequestInfo.sortedPublicDataWritesIndexes,
176
- lowPublicDataWritesPreimages: txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages,
177
- lowPublicDataWritesMembershipWitnesses: txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses,
178
- archiveRootMembershipWitness,
179
- constants,
180
- });
237
+ return PrivateBaseRollupHints.from({
238
+ start,
239
+ stateDiffHints,
240
+ feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint,
241
+ archiveRootMembershipWitness,
242
+ constants,
243
+ });
244
+ }
181
245
  }
182
246
 
183
247
  async function getPublicDataHint(db: MerkleTreeWriteOperations, leafSlot: bigint) {
@@ -416,67 +480,50 @@ export function makeEmptyMembershipWitness<N extends number>(height: N) {
416
480
  }
417
481
 
418
482
  async function processPublicDataUpdateRequests(tx: ProcessedTx, db: MerkleTreeWriteOperations) {
419
- const allPublicDataUpdateRequests = padArrayEnd(
420
- tx.txEffect.publicDataWrites,
421
- PublicDataWrite.empty(),
422
- MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
423
- );
424
-
425
- const allPublicDataWrites = allPublicDataUpdateRequests.map(
483
+ const allPublicDataWrites = tx.txEffect.publicDataWrites.map(
426
484
  ({ leafSlot, value }) => new PublicDataTreeLeaf(leafSlot, value),
427
485
  );
428
- const { lowLeavesWitnessData, newSubtreeSiblingPath, sortedNewLeaves, sortedNewLeavesIndexes } = await db.batchInsert(
429
- MerkleTreeId.PUBLIC_DATA_TREE,
430
- allPublicDataWrites.map(x => x.toBuffer()),
431
- // TODO(#3675) remove oldValue from update requests
432
- PUBLIC_DATA_SUBTREE_HEIGHT,
433
- );
434
-
435
- if (lowLeavesWitnessData === undefined) {
436
- throw new Error(`Could not craft public data batch insertion proofs`);
437
- }
438
-
439
- const sortedPublicDataWrites = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => {
440
- return PublicDataTreeLeaf.fromBuffer(sortedNewLeaves[i]);
441
- });
442
-
443
- const sortedPublicDataWritesIndexes = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => {
444
- return sortedNewLeavesIndexes[i];
445
- });
446
486
 
447
- const subtreeSiblingPathAsFields = newSubtreeSiblingPath.toFields();
448
- const newPublicDataSubtreeSiblingPath = makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, i => {
449
- return subtreeSiblingPathAsFields[i];
450
- });
487
+ const lowPublicDataWritesPreimages = [];
488
+ const lowPublicDataWritesMembershipWitnesses = [];
489
+ const publicDataWritesSiblingPaths = [];
490
+
491
+ for (const write of allPublicDataWrites) {
492
+ if (write.isEmpty()) {
493
+ throw new Error(`Empty public data write in tx: ${toFriendlyJSON(tx)}`);
494
+ }
495
+
496
+ // TODO(Alvaro) write a specialized function for this? Internally add_or_update_value uses batch insertion anyway
497
+ const { lowLeavesWitnessData, newSubtreeSiblingPath } = await db.batchInsert(
498
+ MerkleTreeId.PUBLIC_DATA_TREE,
499
+ [write.toBuffer()],
500
+ // TODO(#3675) remove oldValue from update requests
501
+ 0,
502
+ );
451
503
 
452
- const lowPublicDataWritesMembershipWitnesses: Tuple<
453
- MembershipWitness<typeof PUBLIC_DATA_TREE_HEIGHT>,
454
- typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
455
- > = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => {
456
- const witness = lowLeavesWitnessData[i];
457
- return MembershipWitness.fromBufferArray(
458
- witness.index,
459
- assertLength(witness.siblingPath.toBufferArray(), PUBLIC_DATA_TREE_HEIGHT),
504
+ if (lowLeavesWitnessData === undefined) {
505
+ throw new Error(`Could not craft public data batch insertion proofs`);
506
+ }
507
+
508
+ const [lowLeafWitness] = lowLeavesWitnessData;
509
+ lowPublicDataWritesPreimages.push(lowLeafWitness.leafPreimage as PublicDataTreeLeafPreimage);
510
+ lowPublicDataWritesMembershipWitnesses.push(
511
+ MembershipWitness.fromBufferArray<typeof PUBLIC_DATA_TREE_HEIGHT>(
512
+ lowLeafWitness.index,
513
+ assertLength(lowLeafWitness.siblingPath.toBufferArray(), PUBLIC_DATA_TREE_HEIGHT),
514
+ ),
460
515
  );
461
- });
462
516
 
463
- const lowPublicDataWritesPreimages: Tuple<
464
- PublicDataTreeLeafPreimage,
465
- typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
466
- > = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => {
467
- return lowLeavesWitnessData[i].leafPreimage as PublicDataTreeLeafPreimage;
468
- });
517
+ const insertionSiblingPath = newSubtreeSiblingPath.toFields();
518
+ assertLength(insertionSiblingPath, PUBLIC_DATA_TREE_HEIGHT);
469
519
 
470
- // validate that the sortedPublicDataWrites and sortedPublicDataWritesIndexes are in the correct order
471
- // otherwise it will just fail in the circuit
472
- assertPermutation(allPublicDataWrites, sortedPublicDataWrites, sortedPublicDataWritesIndexes, (a, b) => a.equals(b));
520
+ publicDataWritesSiblingPaths.push(insertionSiblingPath as Tuple<Fr, typeof PUBLIC_DATA_TREE_HEIGHT>);
521
+ }
473
522
 
474
523
  return {
475
524
  lowPublicDataWritesPreimages,
476
525
  lowPublicDataWritesMembershipWitnesses,
477
- newPublicDataSubtreeSiblingPath,
478
- sortedPublicDataWrites,
479
- sortedPublicDataWritesIndexes,
526
+ publicDataWritesSiblingPaths,
480
527
  };
481
528
  }
482
529
 
@@ -13,8 +13,10 @@ import {
13
13
  AvmProofData,
14
14
  type BaseRollupHints,
15
15
  Fr,
16
+ PrivateBaseRollupHints,
16
17
  PrivateBaseRollupInputs,
17
18
  PrivateTubeData,
19
+ PublicBaseRollupHints,
18
20
  PublicBaseRollupInputs,
19
21
  PublicTubeData,
20
22
  type TUBE_PROOF_LENGTH,
@@ -66,6 +68,9 @@ export class TxProvingState {
66
68
  const vkData = this.getTubeVkData();
67
69
  const tubeData = new PrivateTubeData(this.processedTx.data.toKernelCircuitPublicInputs(), this.tube.proof, vkData);
68
70
 
71
+ if (!(this.baseRollupHints instanceof PrivateBaseRollupHints)) {
72
+ throw new Error('Mismatched base rollup hints, expected private base rollup hints');
73
+ }
69
74
  return new PrivateBaseRollupInputs(tubeData, this.baseRollupHints);
70
75
  }
71
76
 
@@ -92,6 +97,10 @@ export class TxProvingState {
92
97
  this.getAvmVkData(),
93
98
  );
94
99
 
100
+ if (!(this.baseRollupHints instanceof PublicBaseRollupHints)) {
101
+ throw new Error('Mismatched base rollup hints, expected public base rollup hints');
102
+ }
103
+
95
104
  return new PublicBaseRollupInputs(tubeData, avmProofData, this.baseRollupHints);
96
105
  }
97
106
 
@@ -0,0 +1,90 @@
1
+ import {
2
+ ProvingError,
3
+ type ProvingRequestType,
4
+ type ServerCircuitProver,
5
+ type V2ProvingJob,
6
+ } from '@aztec/circuit-types';
7
+ import { createDebugLogger } from '@aztec/foundation/log';
8
+ import { RunningPromise } from '@aztec/foundation/running-promise';
9
+
10
+ import { type ProvingJobConsumer } from './proving_broker_interface.js';
11
+ import { ProvingJobController, ProvingJobStatus } from './proving_job_controller.js';
12
+
13
+ /**
14
+ * A helper class that encapsulates a circuit prover and connects it to a job source.
15
+ */
16
+ export class ProvingAgent {
17
+ private currentJobController?: ProvingJobController;
18
+ private runningPromise: RunningPromise;
19
+
20
+ constructor(
21
+ /** The source of proving jobs */
22
+ private jobSource: ProvingJobConsumer,
23
+ /** The prover implementation to defer jobs to */
24
+ private circuitProver: ServerCircuitProver,
25
+ /** Optional list of allowed proof types to build */
26
+ private proofAllowList?: Array<ProvingRequestType>,
27
+ /** How long to wait between jobs */
28
+ private pollIntervalMs = 1000,
29
+ private log = createDebugLogger('aztec:proving-broker:proving-agent'),
30
+ ) {
31
+ this.runningPromise = new RunningPromise(this.safeWork, this.pollIntervalMs);
32
+ }
33
+
34
+ public setCircuitProver(circuitProver: ServerCircuitProver): void {
35
+ this.circuitProver = circuitProver;
36
+ }
37
+
38
+ public isRunning(): boolean {
39
+ return this.runningPromise?.isRunning() ?? false;
40
+ }
41
+
42
+ public start(): void {
43
+ this.runningPromise.start();
44
+ }
45
+
46
+ public async stop(): Promise<void> {
47
+ this.currentJobController?.abort();
48
+ await this.runningPromise.stop();
49
+ }
50
+
51
+ private safeWork = async () => {
52
+ try {
53
+ // every tick we need to
54
+ // (1) either do a heartbeat, telling the broker that we're working
55
+ // (2) get a new job
56
+ // If during (1) the broker returns a new job that means we can cancel the current job and start the new one
57
+ let maybeJob: { job: V2ProvingJob; time: number } | undefined;
58
+ if (this.currentJobController?.getStatus() === ProvingJobStatus.PROVING) {
59
+ maybeJob = await this.jobSource.reportProvingJobProgress(
60
+ this.currentJobController.getJobId(),
61
+ this.currentJobController.getStartedAt(),
62
+ { allowList: this.proofAllowList },
63
+ );
64
+ } else {
65
+ maybeJob = await this.jobSource.getProvingJob({ allowList: this.proofAllowList });
66
+ }
67
+
68
+ if (!maybeJob) {
69
+ return;
70
+ }
71
+
72
+ if (this.currentJobController?.getStatus() === ProvingJobStatus.PROVING) {
73
+ this.currentJobController?.abort();
74
+ }
75
+
76
+ const { job, time } = maybeJob;
77
+ this.currentJobController = new ProvingJobController(job, time, this.circuitProver, (err, result) => {
78
+ if (err) {
79
+ const retry = err.name === ProvingError.NAME ? (err as ProvingError).retry : false;
80
+ return this.jobSource.reportProvingJobError(job.id, err, retry);
81
+ } else if (result) {
82
+ return this.jobSource.reportProvingJobSuccess(job.id, result);
83
+ }
84
+ });
85
+ this.currentJobController.start();
86
+ } catch (err) {
87
+ this.log.error(`Error in ProvingAgent: ${String(err)}`);
88
+ }
89
+ };
90
+ }