@aztec/prover-node 0.69.0 → 0.69.1-devnet
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.
- package/dest/config.d.ts +4 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +17 -2
- package/dest/factory.d.ts +2 -0
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +8 -3
- package/dest/job/epoch-proving-job.d.ts +9 -4
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +72 -25
- package/dest/metrics.d.ts +2 -2
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +9 -4
- package/dest/prover-node.d.ts +23 -15
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +93 -14
- package/dest/test/index.d.ts +10 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +4 -0
- package/package.json +20 -18
- package/src/config.ts +24 -6
- package/src/factory.ts +8 -2
- package/src/job/epoch-proving-job.ts +84 -36
- package/src/metrics.ts +9 -4
- package/src/prover-node.ts +130 -25
- package/src/test/index.ts +11 -0
package/src/metrics.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { type Timer } from '@aztec/foundation/timer';
|
|
2
1
|
import { type Histogram, Metrics, type TelemetryClient, ValueType } from '@aztec/telemetry-client';
|
|
3
2
|
|
|
4
3
|
export class ProverNodeMetrics {
|
|
4
|
+
proverEpochExecutionDuration: Histogram;
|
|
5
5
|
provingJobDuration: Histogram;
|
|
6
6
|
provingJobBlocks: Histogram;
|
|
7
7
|
provingJobTransactions: Histogram;
|
|
8
8
|
|
|
9
9
|
constructor(public readonly client: TelemetryClient, name = 'ProverNode') {
|
|
10
10
|
const meter = client.getMeter(name);
|
|
11
|
+
this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
12
|
+
description: 'Duration of execution of an epoch by the prover',
|
|
13
|
+
unit: 'ms',
|
|
14
|
+
valueType: ValueType.INT,
|
|
15
|
+
});
|
|
11
16
|
this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
12
17
|
description: 'Duration of proving job',
|
|
13
18
|
unit: 'ms',
|
|
@@ -23,9 +28,9 @@ export class ProverNodeMetrics {
|
|
|
23
28
|
});
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
public recordProvingJob(
|
|
27
|
-
|
|
28
|
-
this.provingJobDuration.record(
|
|
31
|
+
public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
|
|
32
|
+
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
|
|
33
|
+
this.provingJobDuration.record(Math.ceil(totalTimeMs));
|
|
29
34
|
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
30
35
|
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
31
36
|
}
|
package/src/prover-node.ts
CHANGED
|
@@ -10,12 +10,19 @@ import {
|
|
|
10
10
|
type ProverCoordination,
|
|
11
11
|
type ProverNodeApi,
|
|
12
12
|
type Service,
|
|
13
|
+
type Tx,
|
|
14
|
+
type TxHash,
|
|
13
15
|
type WorldStateSynchronizer,
|
|
16
|
+
getTimestampRangeForEpoch,
|
|
14
17
|
tryStop,
|
|
15
18
|
} from '@aztec/circuit-types';
|
|
16
19
|
import { type ContractDataSource } from '@aztec/circuits.js';
|
|
20
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
17
21
|
import { compact } from '@aztec/foundation/collection';
|
|
22
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
23
|
+
import { TimeoutError } from '@aztec/foundation/error';
|
|
18
24
|
import { createLogger } from '@aztec/foundation/log';
|
|
25
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
19
26
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
20
27
|
import { type Maybe } from '@aztec/foundation/types';
|
|
21
28
|
import { type P2P } from '@aztec/p2p';
|
|
@@ -35,6 +42,9 @@ export type ProverNodeOptions = {
|
|
|
35
42
|
pollingIntervalMs: number;
|
|
36
43
|
maxPendingJobs: number;
|
|
37
44
|
maxParallelBlocksPerEpoch: number;
|
|
45
|
+
txGatheringTimeoutMs: number;
|
|
46
|
+
txGatheringIntervalMs: number;
|
|
47
|
+
txGatheringMaxParallelRequests: number;
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
/**
|
|
@@ -49,31 +59,35 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
|
|
|
49
59
|
|
|
50
60
|
private latestEpochWeAreProving: bigint | undefined;
|
|
51
61
|
private jobs: Map<string, EpochProvingJob> = new Map();
|
|
62
|
+
private cachedEpochData: { epochNumber: bigint; blocks: L2Block[]; txs: Tx[] } | undefined = undefined;
|
|
52
63
|
private options: ProverNodeOptions;
|
|
53
64
|
private metrics: ProverNodeMetrics;
|
|
54
65
|
|
|
55
66
|
public readonly tracer: Tracer;
|
|
56
67
|
|
|
57
68
|
constructor(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
protected readonly prover: EpochProverManager,
|
|
70
|
+
protected readonly publisher: L1Publisher,
|
|
71
|
+
protected readonly l2BlockSource: L2BlockSource & Maybe<Service>,
|
|
72
|
+
protected readonly l1ToL2MessageSource: L1ToL2MessageSource,
|
|
73
|
+
protected readonly contractDataSource: ContractDataSource,
|
|
74
|
+
protected readonly worldState: WorldStateSynchronizer,
|
|
75
|
+
protected readonly coordination: ProverCoordination & Maybe<Service>,
|
|
76
|
+
protected readonly quoteProvider: QuoteProvider,
|
|
77
|
+
protected readonly quoteSigner: QuoteSigner,
|
|
78
|
+
protected readonly claimsMonitor: ClaimsMonitor,
|
|
79
|
+
protected readonly epochsMonitor: EpochMonitor,
|
|
80
|
+
protected readonly bondManager: BondManager,
|
|
81
|
+
protected readonly telemetryClient: TelemetryClient,
|
|
71
82
|
options: Partial<ProverNodeOptions> = {},
|
|
72
83
|
) {
|
|
73
84
|
this.options = {
|
|
74
85
|
pollingIntervalMs: 1_000,
|
|
75
86
|
maxPendingJobs: 100,
|
|
76
87
|
maxParallelBlocksPerEpoch: 32,
|
|
88
|
+
txGatheringTimeoutMs: 60_000,
|
|
89
|
+
txGatheringIntervalMs: 1_000,
|
|
90
|
+
txGatheringMaxParallelRequests: 100,
|
|
77
91
|
...compact(options),
|
|
78
92
|
};
|
|
79
93
|
|
|
@@ -139,13 +153,12 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
|
|
|
139
153
|
*/
|
|
140
154
|
async handleEpochCompleted(epochNumber: bigint): Promise<void> {
|
|
141
155
|
try {
|
|
142
|
-
//
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
156
|
+
// Gather data for the epoch
|
|
157
|
+
const epochData = await this.gatherEpochData(epochNumber);
|
|
158
|
+
const { blocks } = epochData;
|
|
159
|
+
this.cachedEpochData = { epochNumber, ...epochData };
|
|
148
160
|
|
|
161
|
+
// Construct a quote for the epoch
|
|
149
162
|
const partialQuote = await this.quoteProvider.getQuote(Number(epochNumber), blocks);
|
|
150
163
|
if (!partialQuote) {
|
|
151
164
|
this.log.info(`No quote produced for epoch ${epochNumber}`);
|
|
@@ -256,10 +269,9 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
|
|
|
256
269
|
}
|
|
257
270
|
|
|
258
271
|
// Gather blocks for this epoch
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
272
|
+
const cachedEpochData = this.cachedEpochData?.epochNumber === epochNumber ? this.cachedEpochData : undefined;
|
|
273
|
+
const { blocks, txs } = cachedEpochData ?? (await this.gatherEpochData(epochNumber));
|
|
274
|
+
|
|
263
275
|
const fromBlock = blocks[0].number;
|
|
264
276
|
const toBlock = blocks.at(-1)!.number;
|
|
265
277
|
|
|
@@ -279,15 +291,107 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
|
|
|
279
291
|
return Promise.resolve();
|
|
280
292
|
};
|
|
281
293
|
|
|
282
|
-
const
|
|
294
|
+
const [_, endTimestamp] = getTimestampRangeForEpoch(epochNumber + 1n, await this.getL1Constants());
|
|
295
|
+
const deadline = new Date(Number(endTimestamp) * 1000);
|
|
296
|
+
|
|
297
|
+
const job = this.doCreateEpochProvingJob(epochNumber, deadline, blocks, txs, publicProcessorFactory, cleanUp);
|
|
283
298
|
this.jobs.set(job.getId(), job);
|
|
284
299
|
return job;
|
|
285
300
|
}
|
|
286
301
|
|
|
302
|
+
@memoize
|
|
303
|
+
private getL1Constants() {
|
|
304
|
+
return this.l2BlockSource.getL1Constants();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@trackSpan('ProverNode.gatherEpochData', epochNumber => ({ [Attributes.EPOCH_NUMBER]: Number(epochNumber) }))
|
|
308
|
+
private async gatherEpochData(epochNumber: bigint) {
|
|
309
|
+
// Gather blocks for this epoch and their txs
|
|
310
|
+
const blocks = await this.gatherBlocks(epochNumber);
|
|
311
|
+
const txs = await this.gatherTxs(epochNumber, blocks);
|
|
312
|
+
|
|
313
|
+
return { blocks, txs };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async gatherBlocks(epochNumber: bigint) {
|
|
317
|
+
const blocks = await this.l2BlockSource.getBlocksForEpoch(epochNumber);
|
|
318
|
+
if (blocks.length === 0) {
|
|
319
|
+
throw new Error(`No blocks found for epoch ${epochNumber}`);
|
|
320
|
+
}
|
|
321
|
+
return blocks;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private async gatherTxs(epochNumber: bigint, blocks: L2Block[]) {
|
|
325
|
+
let txsToFind: TxHash[] = [];
|
|
326
|
+
const txHashToBlock = new Map<string, number>();
|
|
327
|
+
const results = new Map<string, Tx>();
|
|
328
|
+
|
|
329
|
+
for (const block of blocks) {
|
|
330
|
+
for (const tx of block.body.txEffects) {
|
|
331
|
+
txsToFind.push(tx.txHash);
|
|
332
|
+
txHashToBlock.set(tx.txHash.toString(), block.number);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const totalTxsRequired = txsToFind.length;
|
|
337
|
+
this.log.info(
|
|
338
|
+
`Gathering a total of ${totalTxsRequired} txs for epoch=${epochNumber} made up of ${blocks.length} blocks`,
|
|
339
|
+
{ epochNumber },
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
let iteration = 0;
|
|
343
|
+
try {
|
|
344
|
+
await retryUntil(
|
|
345
|
+
async () => {
|
|
346
|
+
const batch = [...txsToFind];
|
|
347
|
+
txsToFind = [];
|
|
348
|
+
const batchResults = await asyncPool(this.options.txGatheringMaxParallelRequests, batch, async txHash => {
|
|
349
|
+
const tx = await this.coordination.getTxByHash(txHash);
|
|
350
|
+
return [txHash, tx] as const;
|
|
351
|
+
});
|
|
352
|
+
let found = 0;
|
|
353
|
+
for (const [txHash, maybeTx] of batchResults) {
|
|
354
|
+
if (maybeTx) {
|
|
355
|
+
found++;
|
|
356
|
+
results.set(txHash.toString(), maybeTx);
|
|
357
|
+
} else {
|
|
358
|
+
txsToFind.push(txHash);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
this.log.verbose(
|
|
363
|
+
`Gathered ${found}/${batch.length} txs in iteration ${iteration} for epoch ${epochNumber}. In total ${results.size}/${totalTxsRequired} have been retrieved.`,
|
|
364
|
+
{ epochNumber },
|
|
365
|
+
);
|
|
366
|
+
iteration++;
|
|
367
|
+
|
|
368
|
+
// stop when we found all transactions
|
|
369
|
+
return txsToFind.length === 0;
|
|
370
|
+
},
|
|
371
|
+
'Gather txs',
|
|
372
|
+
this.options.txGatheringTimeoutMs / 1_000,
|
|
373
|
+
this.options.txGatheringIntervalMs / 1_000,
|
|
374
|
+
);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
if (err && err instanceof TimeoutError) {
|
|
377
|
+
const notFoundList = txsToFind
|
|
378
|
+
.map(txHash => `${txHash.toString()} (block ${txHashToBlock.get(txHash.toString())})`)
|
|
379
|
+
.join(', ');
|
|
380
|
+
throw new Error(`Txs not found for epoch ${epochNumber}: ${notFoundList}`);
|
|
381
|
+
} else {
|
|
382
|
+
throw err;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return Array.from(results.values());
|
|
387
|
+
}
|
|
388
|
+
|
|
287
389
|
/** Extracted for testing purposes. */
|
|
288
390
|
protected doCreateEpochProvingJob(
|
|
289
391
|
epochNumber: bigint,
|
|
392
|
+
deadline: Date | undefined,
|
|
290
393
|
blocks: L2Block[],
|
|
394
|
+
txs: Tx[],
|
|
291
395
|
publicProcessorFactory: PublicProcessorFactory,
|
|
292
396
|
cleanUp: () => Promise<void>,
|
|
293
397
|
) {
|
|
@@ -295,13 +399,14 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
|
|
|
295
399
|
this.worldState,
|
|
296
400
|
epochNumber,
|
|
297
401
|
blocks,
|
|
402
|
+
txs,
|
|
298
403
|
this.prover.createEpochProver(),
|
|
299
404
|
publicProcessorFactory,
|
|
300
405
|
this.publisher,
|
|
301
406
|
this.l2BlockSource,
|
|
302
407
|
this.l1ToL2MessageSource,
|
|
303
|
-
this.coordination,
|
|
304
408
|
this.metrics,
|
|
409
|
+
deadline,
|
|
305
410
|
{ parallelBlockLimit: this.options.maxParallelBlocksPerEpoch },
|
|
306
411
|
cleanUp,
|
|
307
412
|
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type EpochProverManager } from '@aztec/circuit-types';
|
|
2
|
+
import { type L1Publisher } from '@aztec/sequencer-client';
|
|
3
|
+
|
|
4
|
+
import { ProverNode } from '../prover-node.js';
|
|
5
|
+
|
|
6
|
+
class TestProverNode_ extends ProverNode {
|
|
7
|
+
public override prover!: EpochProverManager;
|
|
8
|
+
public override publisher!: L1Publisher;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TestProverNode = TestProverNode_;
|