@aztec/prover-node 0.72.1 → 0.73.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.
Files changed (44) hide show
  1. package/dest/bond/escrow-contract.d.ts +2 -5
  2. package/dest/bond/escrow-contract.d.ts.map +1 -1
  3. package/dest/bond/escrow-contract.js +1 -1
  4. package/dest/bond/factory.d.ts +3 -6
  5. package/dest/bond/factory.d.ts.map +1 -1
  6. package/dest/bond/factory.js +2 -4
  7. package/dest/bond/token-contract.d.ts +2 -5
  8. package/dest/bond/token-contract.d.ts.map +1 -1
  9. package/dest/bond/token-contract.js +1 -1
  10. package/dest/factory.d.ts +4 -2
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +13 -9
  13. package/dest/index.d.ts +3 -2
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +4 -3
  16. package/dest/job/epoch-proving-job.d.ts +2 -2
  17. package/dest/job/epoch-proving-job.d.ts.map +1 -1
  18. package/dest/job/epoch-proving-job.js +14 -8
  19. package/dest/metrics.d.ts +13 -1
  20. package/dest/metrics.d.ts.map +1 -1
  21. package/dest/metrics.js +74 -2
  22. package/dest/monitors/claims-monitor.d.ts +2 -2
  23. package/dest/monitors/claims-monitor.d.ts.map +1 -1
  24. package/dest/monitors/claims-monitor.js +1 -1
  25. package/dest/prover-node-publisher.d.ts +67 -0
  26. package/dest/prover-node-publisher.d.ts.map +1 -0
  27. package/dest/prover-node-publisher.js +183 -0
  28. package/dest/prover-node.d.ts +3 -3
  29. package/dest/prover-node.d.ts.map +1 -1
  30. package/dest/prover-node.js +1 -1
  31. package/dest/test/index.d.ts +2 -2
  32. package/dest/test/index.d.ts.map +1 -1
  33. package/package.json +18 -18
  34. package/src/bond/escrow-contract.ts +2 -15
  35. package/src/bond/factory.ts +4 -24
  36. package/src/bond/token-contract.ts +2 -15
  37. package/src/factory.ts +16 -9
  38. package/src/index.ts +3 -2
  39. package/src/job/epoch-proving-job.ts +15 -9
  40. package/src/metrics.ts +107 -1
  41. package/src/monitors/claims-monitor.ts +3 -2
  42. package/src/prover-node-publisher.ts +276 -0
  43. package/src/prover-node.ts +2 -2
  44. package/src/test/index.ts +2 -2
package/src/factory.ts CHANGED
@@ -2,14 +2,14 @@ import { type Archiver, createArchiver } from '@aztec/archiver';
2
2
  import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
3
3
  import { type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types';
4
4
  import { EpochCache } from '@aztec/epoch-cache';
5
- import { createEthereumChain } from '@aztec/ethereum';
5
+ import { L1TxUtils, RollupContract, createEthereumChain, createL1Clients } from '@aztec/ethereum';
6
6
  import { Buffer32 } from '@aztec/foundation/buffer';
7
+ import { EthAddress } from '@aztec/foundation/eth-address';
7
8
  import { type Logger, createLogger } from '@aztec/foundation/log';
8
9
  import { type DataStoreConfig } from '@aztec/kv-store/config';
9
10
  import { RollupAbi } from '@aztec/l1-artifacts';
10
11
  import { createProverClient } from '@aztec/prover-client';
11
12
  import { createAndStartProvingBroker } from '@aztec/prover-client/broker';
12
- import { L1Publisher } from '@aztec/sequencer-client';
13
13
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
14
14
  import { createWorldStateSynchronizer } from '@aztec/world-state';
15
15
 
@@ -20,6 +20,7 @@ import { type ProverNodeConfig, type QuoteProviderConfig } from './config.js';
20
20
  import { ClaimsMonitor } from './monitors/claims-monitor.js';
21
21
  import { EpochMonitor } from './monitors/epoch-monitor.js';
22
22
  import { createProverCoordination } from './prover-coordination/factory.js';
23
+ import { ProverNodePublisher } from './prover-node-publisher.js';
23
24
  import { ProverNode, type ProverNodeOptions } from './prover-node.js';
24
25
  import { HttpQuoteProvider } from './quote-provider/http.js';
25
26
  import { SimpleQuoteProvider } from './quote-provider/simple.js';
@@ -33,13 +34,14 @@ export async function createProverNode(
33
34
  log?: Logger;
34
35
  aztecNodeTxProvider?: ProverCoordination;
35
36
  archiver?: Archiver;
36
- publisher?: L1Publisher;
37
+ publisher?: ProverNodePublisher;
37
38
  blobSinkClient?: BlobSinkClientInterface;
38
39
  broker?: ProvingJobBroker;
40
+ l1TxUtils?: L1TxUtils;
39
41
  } = {},
40
42
  ) {
41
43
  const telemetry = deps.telemetry ?? getTelemetryClient();
42
- const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl);
44
+ const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config);
43
45
  const log = deps.log ?? createLogger('prover-node');
44
46
  const archiver = deps.archiver ?? (await createArchiver(config, blobSinkClient, { blockUntilSync: true }, telemetry));
45
47
  log.verbose(`Created archiver and synced to block ${await archiver.getBlockNumber()}`);
@@ -51,8 +53,14 @@ export async function createProverNode(
51
53
  const broker = deps.broker ?? (await createAndStartProvingBroker(config, telemetry));
52
54
  const prover = await createProverClient(config, worldStateSynchronizer, broker, telemetry);
53
55
 
54
- // REFACTOR: Move publisher out of sequencer package and into an L1-related package
55
- const publisher = deps.publisher ?? new L1Publisher(config, { telemetry, blobSinkClient });
56
+ const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey } = config;
57
+ const chain = createEthereumChain(rpcUrl, chainId);
58
+ const { publicClient, walletClient } = createL1Clients(rpcUrl, publisherPrivateKey, chain.chainInfo);
59
+
60
+ const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
61
+
62
+ const l1TxUtils = deps.l1TxUtils ?? new L1TxUtils(publicClient, walletClient, log, config);
63
+ const publisher = deps.publisher ?? new ProverNodePublisher(config, { telemetry, rollupContract, l1TxUtils });
56
64
 
57
65
  const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config);
58
66
 
@@ -81,9 +89,8 @@ export async function createProverNode(
81
89
  const claimsMonitor = new ClaimsMonitor(publisher, proverNodeConfig, telemetry);
82
90
  const epochMonitor = new EpochMonitor(archiver, proverNodeConfig, telemetry);
83
91
 
84
- const rollupContract = publisher.getRollupContract();
85
- const walletClient = publisher.getClient();
86
- const bondManager = await createBondManager(rollupContract, walletClient, config);
92
+ const escrowContractAddress = await rollupContract.getProofCommitmentEscrow();
93
+ const bondManager = await createBondManager(EthAddress.fromString(escrowContractAddress), walletClient, config);
87
94
 
88
95
  return new ProverNode(
89
96
  prover,
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from './factory.js';
2
1
  export * from './config.js';
3
- export * from './prover-node.js';
2
+ export * from './factory.js';
4
3
  export * from './http.js';
4
+ export * from './prover-node-publisher.js';
5
+ export * from './prover-node.js';
@@ -13,13 +13,13 @@ import { asyncPool } from '@aztec/foundation/async-pool';
13
13
  import { createLogger } from '@aztec/foundation/log';
14
14
  import { promiseWithResolvers } from '@aztec/foundation/promise';
15
15
  import { Timer } from '@aztec/foundation/timer';
16
- import { type L1Publisher } from '@aztec/sequencer-client';
17
16
  import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator/server';
18
17
  import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
19
18
 
20
19
  import * as crypto from 'node:crypto';
21
20
 
22
21
  import { type ProverNodeMetrics } from '../metrics.js';
22
+ import { type ProverNodePublisher } from '../prover-node-publisher.js';
23
23
 
24
24
  /**
25
25
  * Job that grabs a range of blocks from the unfinalised chain from L1, gets their txs given their hashes,
@@ -43,7 +43,7 @@ export class EpochProvingJob implements Traceable {
43
43
  private txs: Tx[],
44
44
  private prover: EpochProver,
45
45
  private publicProcessorFactory: PublicProcessorFactory,
46
- private publisher: L1Publisher,
46
+ private publisher: ProverNodePublisher,
47
47
  private l2BlockSource: L2BlockSource,
48
48
  private l1ToL2MessageSource: L1ToL2MessageSource,
49
49
  private metrics: ProverNodeMetrics,
@@ -91,19 +91,19 @@ export class EpochProvingJob implements Traceable {
91
91
 
92
92
  try {
93
93
  this.prover.startNewEpoch(epochNumber, fromBlock, epochSizeBlocks);
94
- this.prover.startTubeCircuits(this.txs);
94
+ await this.prover.startTubeCircuits(this.txs);
95
95
 
96
96
  await asyncPool(this.config.parallelBlockLimit, this.blocks, async block => {
97
97
  this.checkState();
98
98
 
99
99
  const globalVariables = block.header.globalVariables;
100
- const txs = this.getTxs(block);
100
+ const txs = await this.getTxs(block);
101
101
  const l1ToL2Messages = await this.getL1ToL2Messages(block);
102
102
  const previousHeader = (await this.getBlockHeader(block.number - 1))!;
103
103
 
104
104
  this.log.verbose(`Starting processing block ${block.number}`, {
105
105
  number: block.number,
106
- blockHash: block.hash().toString(),
106
+ blockHash: (await block.hash()).toString(),
107
107
  lastArchive: block.header.lastArchive.root,
108
108
  noteHashTreeRoot: block.header.state.partial.noteHashTree.root,
109
109
  nullifierTreeRoot: block.header.state.partial.nullifierTree.root,
@@ -124,7 +124,7 @@ export class EpochProvingJob implements Traceable {
124
124
  await db.close();
125
125
  this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
126
126
  blockNumber: block.number,
127
- blockHash: block.hash().toString(),
127
+ blockHash: (await block.hash()).toString(),
128
128
  uuid: this.uuid,
129
129
  });
130
130
 
@@ -144,7 +144,10 @@ export class EpochProvingJob implements Traceable {
144
144
  throw new Error('Failed to submit epoch proof to L1');
145
145
  }
146
146
 
147
- this.log.info(`Submitted proof for epoch`, { epochNumber, uuid: this.uuid });
147
+ this.log.info(`Submitted proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`, {
148
+ epochNumber,
149
+ uuid: this.uuid,
150
+ });
148
151
  this.state = 'completed';
149
152
  this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
150
153
  } catch (err: any) {
@@ -210,9 +213,12 @@ export class EpochProvingJob implements Traceable {
210
213
  return this.l2BlockSource.getBlockHeader(blockNumber);
211
214
  }
212
215
 
213
- private getTxs(block: L2Block): Tx[] {
216
+ private async getTxs(block: L2Block): Promise<Tx[]> {
214
217
  const txHashes = block.body.txEffects.map(tx => tx.txHash.toBigInt());
215
- return this.txs.filter(tx => txHashes.includes(tx.getTxHash().toBigInt()));
218
+ const txsAndHashes = await Promise.all(this.txs.map(async tx => ({ tx, hash: await tx.getTxHash() })));
219
+ return txsAndHashes
220
+ .filter(txAndHash => txHashes.includes(txAndHash.hash.toBigInt()))
221
+ .map(txAndHash => txAndHash.tx);
216
222
  }
217
223
 
218
224
  private getL1ToL2Messages(block: L2Block) {
package/src/metrics.ts CHANGED
@@ -1,4 +1,14 @@
1
- import { type Histogram, Metrics, type TelemetryClient, ValueType } from '@aztec/telemetry-client';
1
+ import { type L1PublishProofStats, type L1PublishStats } from '@aztec/circuit-types/stats';
2
+ import {
3
+ Attributes,
4
+ type Histogram,
5
+ Metrics,
6
+ type TelemetryClient,
7
+ type UpDownCounter,
8
+ ValueType,
9
+ } from '@aztec/telemetry-client';
10
+
11
+ import { formatEther } from 'viem';
2
12
 
3
13
  export class ProverNodeMetrics {
4
14
  proverEpochExecutionDuration: Histogram;
@@ -6,6 +16,15 @@ export class ProverNodeMetrics {
6
16
  provingJobBlocks: Histogram;
7
17
  provingJobTransactions: Histogram;
8
18
 
19
+ gasPrice: Histogram;
20
+ txCount: UpDownCounter;
21
+ txDuration: Histogram;
22
+ txGas: Histogram;
23
+ txCalldataSize: Histogram;
24
+ txCalldataGas: Histogram;
25
+ txBlobDataGasUsed: Histogram;
26
+ txBlobDataGasCost: Histogram;
27
+
9
28
  constructor(public readonly client: TelemetryClient, name = 'ProverNode') {
10
29
  const meter = client.getMeter(name);
11
30
  this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
@@ -26,6 +45,63 @@ export class ProverNodeMetrics {
26
45
  description: 'Number of transactions in a proven epoch',
27
46
  valueType: ValueType.INT,
28
47
  });
48
+
49
+ this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
50
+ description: 'The gas price used for transactions',
51
+ unit: 'gwei',
52
+ valueType: ValueType.DOUBLE,
53
+ });
54
+
55
+ this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
56
+ description: 'The number of transactions processed',
57
+ });
58
+
59
+ this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
60
+ description: 'The duration of transaction processing',
61
+ unit: 'ms',
62
+ valueType: ValueType.INT,
63
+ });
64
+
65
+ this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
66
+ description: 'The gas consumed by transactions',
67
+ unit: 'gas',
68
+ valueType: ValueType.INT,
69
+ });
70
+
71
+ this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
72
+ description: 'The size of the calldata in transactions',
73
+ unit: 'By',
74
+ valueType: ValueType.INT,
75
+ });
76
+
77
+ this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
78
+ description: 'The gas consumed by the calldata in transactions',
79
+ unit: 'gas',
80
+ valueType: ValueType.INT,
81
+ });
82
+
83
+ this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
84
+ description: 'The amount of blob gas used in transactions',
85
+ unit: 'gas',
86
+ valueType: ValueType.INT,
87
+ });
88
+
89
+ this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
90
+ description: 'The gas cost of blobs in transactions',
91
+ unit: 'gwei',
92
+ valueType: ValueType.INT,
93
+ });
94
+ }
95
+
96
+ recordFailedTx() {
97
+ this.txCount.add(1, {
98
+ [Attributes.L1_TX_TYPE]: 'submitProof',
99
+ [Attributes.OK]: false,
100
+ });
101
+ }
102
+
103
+ recordSubmitProof(durationMs: number, stats: L1PublishProofStats) {
104
+ this.recordTx(durationMs, stats);
29
105
  }
30
106
 
31
107
  public recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number) {
@@ -34,4 +110,34 @@ export class ProverNodeMetrics {
34
110
  this.provingJobBlocks.record(Math.floor(numBlocks));
35
111
  this.provingJobTransactions.record(Math.floor(numTxs));
36
112
  }
113
+
114
+ private recordTx(durationMs: number, stats: L1PublishStats) {
115
+ const attributes = {
116
+ [Attributes.L1_TX_TYPE]: 'submitProof',
117
+ [Attributes.L1_SENDER]: stats.sender,
118
+ } as const;
119
+
120
+ this.txCount.add(1, {
121
+ ...attributes,
122
+ [Attributes.OK]: true,
123
+ });
124
+
125
+ this.txDuration.record(Math.ceil(durationMs), attributes);
126
+ this.txGas.record(
127
+ // safe to downcast - total block limit is 30M gas which fits in a JS number
128
+ Number(stats.gasUsed),
129
+ attributes,
130
+ );
131
+ this.txCalldataGas.record(stats.calldataGas, attributes);
132
+ this.txCalldataSize.record(stats.calldataSize, attributes);
133
+
134
+ this.txBlobDataGasCost.record(Number(stats.blobDataGas), attributes);
135
+ this.txBlobDataGasUsed.record(Number(stats.blobGasUsed), attributes);
136
+
137
+ try {
138
+ this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
139
+ } catch (e) {
140
+ // ignore
141
+ }
142
+ }
37
143
  }
@@ -2,7 +2,6 @@ import { type EpochProofClaim } from '@aztec/circuit-types';
2
2
  import { type EthAddress } from '@aztec/circuits.js';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
- import { type L1Publisher } from '@aztec/sequencer-client';
6
5
  import {
7
6
  type TelemetryClient,
8
7
  type Traceable,
@@ -11,6 +10,8 @@ import {
11
10
  trackSpan,
12
11
  } from '@aztec/telemetry-client';
13
12
 
13
+ import { type ProverNodePublisher } from '../prover-node-publisher.js';
14
+
14
15
  export interface ClaimsMonitorHandler {
15
16
  handleClaim(proofClaim: EpochProofClaim): Promise<void>;
16
17
  }
@@ -25,7 +26,7 @@ export class ClaimsMonitor implements Traceable {
25
26
  public readonly tracer: Tracer;
26
27
 
27
28
  constructor(
28
- private readonly l1Publisher: L1Publisher,
29
+ private readonly l1Publisher: ProverNodePublisher,
29
30
  private options: { pollingIntervalMs: number },
30
31
  telemetry: TelemetryClient = getTelemetryClient(),
31
32
  ) {
@@ -0,0 +1,276 @@
1
+ import { type L1PublishProofStats } from '@aztec/circuit-types/stats';
2
+ import { AGGREGATION_OBJECT_LENGTH, AZTEC_MAX_EPOCH_DURATION, type Proof } from '@aztec/circuits.js';
3
+ import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup';
4
+ import { type L1TxUtils, type RollupContract } from '@aztec/ethereum';
5
+ import { makeTuple } from '@aztec/foundation/array';
6
+ import { areArraysEqual, times } from '@aztec/foundation/collection';
7
+ import { EthAddress } from '@aztec/foundation/eth-address';
8
+ import { Fr } from '@aztec/foundation/fields';
9
+ import { createLogger } from '@aztec/foundation/log';
10
+ import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
11
+ import { InterruptibleSleep } from '@aztec/foundation/sleep';
12
+ import { Timer } from '@aztec/foundation/timer';
13
+ import { RollupAbi } from '@aztec/l1-artifacts';
14
+ import { type PublisherConfig, type TxSenderConfig } from '@aztec/sequencer-client';
15
+ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
16
+
17
+ import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
18
+
19
+ import { ProverNodeMetrics } from './metrics.js';
20
+
21
+ /**
22
+ * Stats for a sent transaction.
23
+ */
24
+ /** Arguments to the submitEpochProof method of the rollup contract */
25
+ export type L1SubmitEpochProofArgs = {
26
+ epochSize: number;
27
+ previousArchive: Fr;
28
+ endArchive: Fr;
29
+ previousBlockHash: Fr;
30
+ endBlockHash: Fr;
31
+ endTimestamp: Fr;
32
+ outHash: Fr;
33
+ proverId: Fr;
34
+ fees: Tuple<FeeRecipient, typeof AZTEC_MAX_EPOCH_DURATION>;
35
+ proof: Proof;
36
+ };
37
+
38
+ export class ProverNodePublisher {
39
+ private interruptibleSleep = new InterruptibleSleep();
40
+ private sleepTimeMs: number;
41
+ private interrupted = false;
42
+ private metrics: ProverNodeMetrics;
43
+
44
+ protected log = createLogger('prover-node:l1-tx-publisher');
45
+
46
+ protected rollupContract: RollupContract;
47
+
48
+ public readonly l1TxUtils: L1TxUtils;
49
+
50
+ constructor(
51
+ config: TxSenderConfig & PublisherConfig,
52
+ deps: {
53
+ rollupContract: RollupContract;
54
+ l1TxUtils: L1TxUtils;
55
+ telemetry?: TelemetryClient;
56
+ },
57
+ ) {
58
+ this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
59
+
60
+ const telemetry = deps.telemetry ?? getTelemetryClient();
61
+
62
+ this.metrics = new ProverNodeMetrics(telemetry, 'ProverNode');
63
+
64
+ this.rollupContract = deps.rollupContract;
65
+ this.l1TxUtils = deps.l1TxUtils;
66
+ }
67
+
68
+ /**
69
+ * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
70
+ * Be warned, the call may return false even if the tx subsequently gets successfully mined.
71
+ * In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
72
+ * A call to `restart` is required before you can continue publishing.
73
+ */
74
+ public interrupt() {
75
+ this.interrupted = true;
76
+ this.interruptibleSleep.interrupt();
77
+ }
78
+
79
+ /** Restarts the publisher after calling `interrupt`. */
80
+ public restart() {
81
+ this.interrupted = false;
82
+ }
83
+
84
+ public getSenderAddress() {
85
+ return EthAddress.fromString(this.l1TxUtils.getSenderAddress());
86
+ }
87
+
88
+ public getProofClaim() {
89
+ return this.rollupContract.getProofClaim();
90
+ }
91
+
92
+ public async submitEpochProof(args: {
93
+ epochNumber: number;
94
+ fromBlock: number;
95
+ toBlock: number;
96
+ publicInputs: RootRollupPublicInputs;
97
+ proof: Proof;
98
+ }): Promise<boolean> {
99
+ const { epochNumber, fromBlock, toBlock } = args;
100
+ const ctx = { epochNumber, fromBlock, toBlock };
101
+ if (!this.interrupted) {
102
+ const timer = new Timer();
103
+
104
+ // Validate epoch proof range and hashes are correct before submitting
105
+ await this.validateEpochProofSubmission(args);
106
+
107
+ const txReceipt = await this.sendSubmitEpochProofTx(args);
108
+ if (!txReceipt) {
109
+ return false;
110
+ }
111
+
112
+ // Tx was mined successfully
113
+ if (txReceipt.status) {
114
+ const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
115
+ const stats: L1PublishProofStats = {
116
+ gasPrice: txReceipt.effectiveGasPrice,
117
+ gasUsed: txReceipt.gasUsed,
118
+ transactionHash: txReceipt.transactionHash,
119
+ calldataGas: tx!.calldataGas,
120
+ calldataSize: tx!.calldataSize,
121
+ sender: tx!.sender,
122
+ blobDataGas: 0n,
123
+ blobGasUsed: 0n,
124
+ eventName: 'proof-published-to-l1',
125
+ };
126
+ this.log.info(`Published epoch proof to L1 rollup contract`, { ...stats, ...ctx });
127
+ this.metrics.recordSubmitProof(timer.ms(), stats);
128
+ return true;
129
+ }
130
+
131
+ this.metrics.recordFailedTx();
132
+ this.log.error(`Rollup.submitEpochProof tx status failed: ${txReceipt.transactionHash}`, ctx);
133
+ await this.sleepOrInterrupted();
134
+ }
135
+
136
+ this.log.verbose('L2 block data syncing interrupted while processing blocks.', ctx);
137
+ return false;
138
+ }
139
+
140
+ private async validateEpochProofSubmission(args: {
141
+ fromBlock: number;
142
+ toBlock: number;
143
+ publicInputs: RootRollupPublicInputs;
144
+ proof: Proof;
145
+ }) {
146
+ const { fromBlock, toBlock, publicInputs, proof } = args;
147
+
148
+ // Check that the block numbers match the expected epoch to be proven
149
+ const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
150
+ if (proven !== BigInt(fromBlock) - 1n) {
151
+ throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
152
+ }
153
+ if (toBlock > pending) {
154
+ throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
155
+ }
156
+
157
+ // Check the block hash and archive for the immediate block before the epoch
158
+ const blockLog = await this.rollupContract.getBlock(proven);
159
+ if (publicInputs.previousArchive.root.toString() !== blockLog.archive) {
160
+ throw new Error(
161
+ `Previous archive root mismatch: ${publicInputs.previousArchive.root.toString()} !== ${blockLog.archive}`,
162
+ );
163
+ }
164
+ // TODO: Remove zero check once we inject the proper zero blockhash
165
+ if (blockLog.blockHash !== Fr.ZERO.toString() && publicInputs.previousBlockHash.toString() !== blockLog.blockHash) {
166
+ throw new Error(
167
+ `Previous block hash mismatch: ${publicInputs.previousBlockHash.toString()} !== ${blockLog.blockHash}`,
168
+ );
169
+ }
170
+
171
+ // Check the block hash and archive for the last block in the epoch
172
+ const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
173
+ if (publicInputs.endArchive.root.toString() !== endBlockLog.archive) {
174
+ throw new Error(
175
+ `End archive root mismatch: ${publicInputs.endArchive.root.toString()} !== ${endBlockLog.archive}`,
176
+ );
177
+ }
178
+ if (publicInputs.endBlockHash.toString() !== endBlockLog.blockHash) {
179
+ throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockLog.blockHash}`);
180
+ }
181
+
182
+ // Compare the public inputs computed by the contract with the ones injected
183
+ const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(this.getSubmitEpochProofArgs(args));
184
+ const aggregationObject = proof.isEmpty()
185
+ ? times(AGGREGATION_OBJECT_LENGTH, Fr.zero)
186
+ : proof.extractAggregationObject();
187
+ const argsPublicInputs = [...publicInputs.toFields(), ...aggregationObject];
188
+
189
+ if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b) => a.equals(b))) {
190
+ const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', ');
191
+ throw new Error(
192
+ `Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`,
193
+ );
194
+ }
195
+ }
196
+
197
+ private async sendSubmitEpochProofTx(args: {
198
+ fromBlock: number;
199
+ toBlock: number;
200
+ publicInputs: RootRollupPublicInputs;
201
+ proof: Proof;
202
+ }): Promise<TransactionReceipt | undefined> {
203
+ const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
204
+ const argsArray = this.getSubmitEpochProofArgs(args);
205
+
206
+ const txArgs = [
207
+ {
208
+ epochSize: argsArray[0],
209
+ args: argsArray[1],
210
+ fees: argsArray[2],
211
+ blobPublicInputs: argsArray[3],
212
+ aggregationObject: argsArray[4],
213
+ proof: proofHex,
214
+ },
215
+ ] as const;
216
+
217
+ this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
218
+ const data = encodeFunctionData({
219
+ abi: RollupAbi,
220
+ functionName: 'submitEpochRootProof',
221
+ args: txArgs,
222
+ });
223
+ try {
224
+ const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
225
+ to: this.rollupContract.address,
226
+ data,
227
+ });
228
+
229
+ return receipt;
230
+ } catch (err) {
231
+ this.log.error(`Rollup submit epoch proof failed`, err);
232
+ const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(data, {
233
+ args: [...txArgs],
234
+ functionName: 'submitEpochRootProof',
235
+ abi: RollupAbi,
236
+ address: this.rollupContract.address,
237
+ });
238
+ this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
239
+ return undefined;
240
+ }
241
+ }
242
+
243
+ private getSubmitEpochProofArgs(args: {
244
+ fromBlock: number;
245
+ toBlock: number;
246
+ publicInputs: RootRollupPublicInputs;
247
+ proof: Proof;
248
+ }) {
249
+ return [
250
+ BigInt(args.toBlock - args.fromBlock + 1),
251
+ [
252
+ args.publicInputs.previousArchive.root.toString(),
253
+ args.publicInputs.endArchive.root.toString(),
254
+ args.publicInputs.previousBlockHash.toString(),
255
+ args.publicInputs.endBlockHash.toString(),
256
+ args.publicInputs.endTimestamp.toString(),
257
+ args.publicInputs.outHash.toString(),
258
+ args.publicInputs.proverId.toString(),
259
+ ],
260
+ makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i =>
261
+ i % 2 === 0
262
+ ? args.publicInputs.fees[i / 2].recipient.toField().toString()
263
+ : args.publicInputs.fees[(i - 1) / 2].value.toString(),
264
+ ),
265
+ `0x${args.publicInputs.blobPublicInputs
266
+ .filter((_, i) => i < args.toBlock - args.fromBlock + 1)
267
+ .map(b => b.toString())
268
+ .join(``)}`,
269
+ `0x${serializeToBuffer(args.proof.extractAggregationObject()).toString('hex')}`,
270
+ ] as const;
271
+ }
272
+
273
+ protected async sleepOrInterrupted() {
274
+ await this.interruptibleSleep.sleep(this.sleepTimeMs);
275
+ }
276
+ }
@@ -26,7 +26,6 @@ import { retryUntil } from '@aztec/foundation/retry';
26
26
  import { DateProvider } from '@aztec/foundation/timer';
27
27
  import { type Maybe } from '@aztec/foundation/types';
28
28
  import { type P2P } from '@aztec/p2p';
29
- import { type L1Publisher } from '@aztec/sequencer-client';
30
29
  import { PublicProcessorFactory } from '@aztec/simulator/server';
31
30
  import {
32
31
  Attributes,
@@ -42,6 +41,7 @@ import { EpochProvingJob, type EpochProvingJobState } from './job/epoch-proving-
42
41
  import { ProverNodeMetrics } from './metrics.js';
43
42
  import { type ClaimsMonitor, type ClaimsMonitorHandler } from './monitors/claims-monitor.js';
44
43
  import { type EpochMonitor, type EpochMonitorHandler } from './monitors/epoch-monitor.js';
44
+ import { type ProverNodePublisher } from './prover-node-publisher.js';
45
45
  import { type QuoteProvider } from './quote-provider/index.js';
46
46
  import { type QuoteSigner } from './quote-signer.js';
47
47
 
@@ -74,7 +74,7 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr
74
74
 
75
75
  constructor(
76
76
  protected readonly prover: EpochProverManager,
77
- protected readonly publisher: L1Publisher,
77
+ protected readonly publisher: ProverNodePublisher,
78
78
  protected readonly l2BlockSource: L2BlockSource & Maybe<Service>,
79
79
  protected readonly l1ToL2MessageSource: L1ToL2MessageSource,
80
80
  protected readonly contractDataSource: ContractDataSource,
package/src/test/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { type EpochProverManager } from '@aztec/circuit-types';
2
- import { type L1Publisher } from '@aztec/sequencer-client';
3
2
 
3
+ import { type ProverNodePublisher } from '../prover-node-publisher.js';
4
4
  import { ProverNode } from '../prover-node.js';
5
5
 
6
6
  class TestProverNode_ extends ProverNode {
7
7
  public override prover!: EpochProverManager;
8
- public override publisher!: L1Publisher;
8
+ public override publisher!: ProverNodePublisher;
9
9
  }
10
10
 
11
11
  export type TestProverNode = TestProverNode_;