@aztec/prover-node 0.0.1-commit.2ed92850 → 0.0.1-commit.3100065
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/README.md +511 -0
- package/dest/actions/download-epoch-proving-job.js +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +5 -4
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +102 -22
- package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
- package/dest/bin/run-failed-epoch.js +6 -5
- package/dest/checkpoint-store.d.ts +88 -0
- package/dest/checkpoint-store.d.ts.map +1 -0
- package/dest/checkpoint-store.js +169 -0
- package/dest/config.d.ts +5 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +17 -20
- package/dest/factory.d.ts +19 -13
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +40 -65
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/job/checkpoint-prover.d.ts +124 -0
- package/dest/job/checkpoint-prover.d.ts.map +1 -0
- package/dest/job/checkpoint-prover.js +330 -0
- package/dest/job/epoch-session.d.ts +146 -0
- package/dest/job/epoch-session.d.ts.map +1 -0
- package/dest/job/{epoch-proving-job.js → epoch-session.js} +277 -295
- package/dest/job/top-tree-job.d.ts +82 -0
- package/dest/job/top-tree-job.d.ts.map +1 -0
- package/dest/job/top-tree-job.js +152 -0
- package/dest/metrics.d.ts +40 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +112 -7
- package/dest/monitors/epoch-monitor.d.ts +1 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +11 -9
- package/dest/proof-publishing-service.d.ts +161 -0
- package/dest/proof-publishing-service.d.ts.map +1 -0
- package/dest/proof-publishing-service.js +335 -0
- package/dest/prover-node-publisher.d.ts +25 -17
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +201 -63
- package/dest/prover-node.d.ts +118 -70
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +487 -226
- package/dest/prover-publisher-factory.d.ts +7 -5
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +7 -5
- package/dest/session-manager.d.ts +158 -0
- package/dest/session-manager.d.ts.map +1 -0
- package/dest/session-manager.js +482 -0
- package/dest/test/index.d.ts +7 -6
- package/dest/test/index.d.ts.map +1 -1
- package/package.json +23 -22
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +115 -28
- package/src/actions/upload-epoch-proof-failure.ts +1 -1
- package/src/bin/run-failed-epoch.ts +5 -3
- package/src/checkpoint-store.ts +194 -0
- package/src/config.ts +25 -32
- package/src/factory.ts +68 -111
- package/src/index.ts +1 -0
- package/src/job/checkpoint-prover.ts +442 -0
- package/src/job/epoch-session.ts +436 -0
- package/src/job/top-tree-job.ts +227 -0
- package/src/metrics.ts +129 -12
- package/src/monitors/epoch-monitor.ts +5 -6
- package/src/proof-publishing-service.ts +427 -0
- package/src/prover-node-publisher.ts +236 -80
- package/src/prover-node.ts +562 -254
- package/src/prover-publisher-factory.ts +16 -10
- package/src/session-manager.ts +583 -0
- package/src/test/index.ts +6 -6
- package/dest/job/epoch-proving-job.d.ts +0 -62
- package/dest/job/epoch-proving-job.d.ts.map +0 -1
- package/src/job/epoch-proving-job.ts +0 -430
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import { BatchedBlob, getEthBlobEvaluationInputs } from '@aztec/blob-lib';
|
|
2
|
-
import {
|
|
2
|
+
import { MAX_CHECKPOINTS_PER_EPOCH } from '@aztec/constants';
|
|
3
3
|
import type { RollupContract, ViemCommitteeAttestation } from '@aztec/ethereum/contracts';
|
|
4
4
|
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
5
|
-
import { makeTuple } from '@aztec/foundation/array';
|
|
6
5
|
import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
7
6
|
import { areArraysEqual } from '@aztec/foundation/collection';
|
|
8
7
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
9
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
10
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
11
|
-
import type { Tuple } from '@aztec/foundation/serialize';
|
|
9
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
12
10
|
import { Timer } from '@aztec/foundation/timer';
|
|
13
11
|
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
14
12
|
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
|
|
15
13
|
import { CommitteeAttestation, CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
16
14
|
import type { Proof } from '@aztec/stdlib/proofs';
|
|
17
|
-
import type {
|
|
15
|
+
import type { CheckpointHeader, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
|
|
18
16
|
import type { L1PublishProofStats } from '@aztec/stdlib/stats';
|
|
19
17
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
20
18
|
|
|
21
19
|
import { inspect } from 'util';
|
|
22
|
-
import { type Hex, type TransactionReceipt, encodeFunctionData } from 'viem';
|
|
20
|
+
import { type Hex, type TransactionReceipt, encodeFunctionData, formatEther, formatGwei } from 'viem';
|
|
23
21
|
|
|
24
|
-
import { ProverNodePublisherMetrics } from './metrics.js';
|
|
22
|
+
import { type EstimatedSubmitProofStats, ProverNodePublisherMetrics } from './metrics.js';
|
|
25
23
|
|
|
26
24
|
/** Arguments to the submitEpochProof method of the rollup contract */
|
|
27
25
|
export type L1SubmitEpochProofArgs = {
|
|
@@ -31,15 +29,14 @@ export type L1SubmitEpochProofArgs = {
|
|
|
31
29
|
endTimestamp: Fr;
|
|
32
30
|
outHash: Fr;
|
|
33
31
|
proverId: Fr;
|
|
34
|
-
|
|
32
|
+
headers: CheckpointHeader[];
|
|
35
33
|
proof: Proof;
|
|
36
34
|
};
|
|
37
35
|
|
|
38
36
|
export class ProverNodePublisher {
|
|
39
|
-
private interrupted = false;
|
|
40
37
|
private metrics: ProverNodePublisherMetrics;
|
|
41
38
|
|
|
42
|
-
protected log
|
|
39
|
+
protected log: Logger;
|
|
43
40
|
|
|
44
41
|
protected rollupContract: RollupContract;
|
|
45
42
|
|
|
@@ -52,10 +49,12 @@ export class ProverNodePublisher {
|
|
|
52
49
|
l1TxUtils: L1TxUtils;
|
|
53
50
|
telemetry?: TelemetryClient;
|
|
54
51
|
},
|
|
52
|
+
bindings?: LoggerBindings,
|
|
55
53
|
) {
|
|
56
54
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
57
55
|
|
|
58
56
|
this.metrics = new ProverNodePublisherMetrics(telemetry, 'ProverNode');
|
|
57
|
+
this.log = createLogger('prover-node:l1-tx-publisher', bindings);
|
|
59
58
|
|
|
60
59
|
this.rollupContract = deps.rollupContract;
|
|
61
60
|
this.l1TxUtils = deps.l1TxUtils;
|
|
@@ -65,23 +64,6 @@ export class ProverNodePublisher {
|
|
|
65
64
|
return this.rollupContract;
|
|
66
65
|
}
|
|
67
66
|
|
|
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.l1TxUtils.interrupt();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Restarts the publisher after calling `interrupt`. */
|
|
80
|
-
public restart() {
|
|
81
|
-
this.interrupted = false;
|
|
82
|
-
this.l1TxUtils.restart();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
67
|
public getSenderAddress() {
|
|
86
68
|
return this.l1TxUtils.getSenderAddress();
|
|
87
69
|
}
|
|
@@ -94,54 +76,53 @@ export class ProverNodePublisher {
|
|
|
94
76
|
proof: Proof;
|
|
95
77
|
batchedBlobInputs: BatchedBlob;
|
|
96
78
|
attestations: ViemCommitteeAttestation[];
|
|
79
|
+
headers: CheckpointHeader[];
|
|
80
|
+
/** Wall-clock deadline (proof-submission window end) past which the L1 tx should stop retrying. */
|
|
81
|
+
deadline?: Date;
|
|
97
82
|
}): Promise<boolean> {
|
|
98
83
|
const { epochNumber, fromCheckpoint, toCheckpoint } = args;
|
|
99
84
|
const ctx = { epochNumber, fromCheckpoint, toCheckpoint };
|
|
100
85
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
await this.validateEpochProofSubmission(args);
|
|
105
|
-
|
|
106
|
-
const txReceipt = await this.sendSubmitEpochProofTx(args);
|
|
107
|
-
if (!txReceipt) {
|
|
108
|
-
this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
86
|
+
const timer = new Timer();
|
|
87
|
+
// Validate epoch proof range and hashes are correct before submitting
|
|
88
|
+
await this.validateEpochProofSubmission(args);
|
|
111
89
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
} catch (err) {
|
|
118
|
-
this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
|
|
119
|
-
}
|
|
90
|
+
const txReceipt = await this.sendSubmitEpochProofTx(args);
|
|
91
|
+
if (!txReceipt) {
|
|
92
|
+
this.log.error(`Failed to mine submitEpochProof tx`, undefined, ctx);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
120
95
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
calldataSize: tx!.calldataSize,
|
|
130
|
-
sender: tx!.sender,
|
|
131
|
-
blobDataGas: 0n,
|
|
132
|
-
blobGasUsed: 0n,
|
|
133
|
-
eventName: 'proof-published-to-l1',
|
|
134
|
-
};
|
|
135
|
-
this.log.info(`Published epoch proof to L1 rollup contract`, { ...stats, ...ctx });
|
|
136
|
-
this.metrics.recordSubmitProof(timer.ms(), stats);
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
96
|
+
try {
|
|
97
|
+
this.metrics.recordSenderBalance(
|
|
98
|
+
await this.l1TxUtils.getSenderBalance(),
|
|
99
|
+
this.l1TxUtils.getSenderAddress().toString(),
|
|
100
|
+
);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
|
|
103
|
+
}
|
|
139
104
|
|
|
140
|
-
|
|
141
|
-
|
|
105
|
+
// Tx was mined successfully
|
|
106
|
+
if (txReceipt.status === 'success') {
|
|
107
|
+
const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
|
|
108
|
+
const stats: L1PublishProofStats = {
|
|
109
|
+
gasPrice: txReceipt.effectiveGasPrice,
|
|
110
|
+
gasUsed: txReceipt.gasUsed,
|
|
111
|
+
transactionHash: txReceipt.transactionHash,
|
|
112
|
+
calldataGas: tx!.calldataGas,
|
|
113
|
+
calldataSize: tx!.calldataSize,
|
|
114
|
+
sender: tx!.sender,
|
|
115
|
+
blobDataGas: 0n,
|
|
116
|
+
blobGasUsed: 0n,
|
|
117
|
+
eventName: 'proof-published-to-l1',
|
|
118
|
+
};
|
|
119
|
+
this.log.info(`Published epoch proof to L1 rollup contract`, { ...stats, ...ctx });
|
|
120
|
+
this.metrics.recordSubmitProof(timer.ms(), stats);
|
|
121
|
+
return true;
|
|
142
122
|
}
|
|
143
123
|
|
|
144
|
-
this.
|
|
124
|
+
this.metrics.recordFailedTx();
|
|
125
|
+
this.log.error(`Rollup submitEpochProof tx reverted ${txReceipt.transactionHash}`, undefined, ctx);
|
|
145
126
|
return false;
|
|
146
127
|
}
|
|
147
128
|
|
|
@@ -152,6 +133,7 @@ export class ProverNodePublisher {
|
|
|
152
133
|
proof: Proof;
|
|
153
134
|
batchedBlobInputs: BatchedBlob;
|
|
154
135
|
attestations: ViemCommitteeAttestation[];
|
|
136
|
+
headers: CheckpointHeader[];
|
|
155
137
|
}) {
|
|
156
138
|
const { fromCheckpoint, toCheckpoint, publicInputs, batchedBlobInputs } = args;
|
|
157
139
|
|
|
@@ -166,7 +148,7 @@ export class ProverNodePublisher {
|
|
|
166
148
|
// toCheckpoint can't be greater than pending
|
|
167
149
|
if (toCheckpoint > pending) {
|
|
168
150
|
throw new Error(
|
|
169
|
-
`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as
|
|
151
|
+
`Cannot submit epoch proof for ${fromCheckpoint}-${toCheckpoint} as proposed checkpoint is ${pending}`,
|
|
170
152
|
);
|
|
171
153
|
}
|
|
172
154
|
|
|
@@ -201,20 +183,96 @@ export class ProverNodePublisher {
|
|
|
201
183
|
const argsPublicInputs = [...publicInputs.toFields()];
|
|
202
184
|
|
|
203
185
|
if (!areArraysEqual(rollupPublicInputs, argsPublicInputs, (a, b) => a.equals(b))) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
186
|
+
throw await reportPublicInputsMismatch({
|
|
187
|
+
rollupPublicInputs,
|
|
188
|
+
argsPublicInputs,
|
|
189
|
+
fromCheckpoint,
|
|
190
|
+
toCheckpoint,
|
|
191
|
+
rollupContract: this.rollupContract,
|
|
192
|
+
log: this.log,
|
|
193
|
+
});
|
|
208
194
|
}
|
|
209
195
|
}
|
|
210
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Estimates what submitting the epoch proof would have cost on L1 without actually sending it.
|
|
199
|
+
* Runs the same validation as `submitEpochProof`, encodes the calldata, estimates gas, and records metrics.
|
|
200
|
+
* Used when proof publishing is disabled (e.g. PROVER_NODE_DISABLE_PROOF_PUBLISH=true on mainnet).
|
|
201
|
+
*/
|
|
202
|
+
public async analyzeEpochProofSubmission(args: {
|
|
203
|
+
epochNumber: EpochNumber;
|
|
204
|
+
fromCheckpoint: CheckpointNumber;
|
|
205
|
+
toCheckpoint: CheckpointNumber;
|
|
206
|
+
publicInputs: RootRollupPublicInputs;
|
|
207
|
+
proof: Proof;
|
|
208
|
+
batchedBlobInputs: BatchedBlob;
|
|
209
|
+
attestations: ViemCommitteeAttestation[];
|
|
210
|
+
headers: CheckpointHeader[];
|
|
211
|
+
}): Promise<void> {
|
|
212
|
+
const { epochNumber, fromCheckpoint, toCheckpoint } = args;
|
|
213
|
+
|
|
214
|
+
await this.validateEpochProofSubmission(args);
|
|
215
|
+
|
|
216
|
+
const data = this.encodeSubmitEpochProofCalldata(args);
|
|
217
|
+
const senderAddress = this.l1TxUtils.getSenderAddress();
|
|
218
|
+
|
|
219
|
+
const [gasLimit, gasPrice, latestBlock] = await Promise.all([
|
|
220
|
+
this.l1TxUtils.estimateGas(senderAddress.toString() as `0x${string}`, { to: this.rollupContract.address, data }),
|
|
221
|
+
this.l1TxUtils.getGasPrice(),
|
|
222
|
+
this.l1TxUtils.client.getBlock({ blockTag: 'latest' }),
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
const baseFeePerGas = latestBlock.baseFeePerGas ?? 0n;
|
|
226
|
+
const { maxPriorityFeePerGas } = gasPrice;
|
|
227
|
+
|
|
228
|
+
const effectiveFeePerGas = baseFeePerGas + maxPriorityFeePerGas;
|
|
229
|
+
const estimatedTotalFee = gasLimit * effectiveFeePerGas;
|
|
230
|
+
|
|
231
|
+
const stats: EstimatedSubmitProofStats = {
|
|
232
|
+
gasLimit,
|
|
233
|
+
baseFeePerGas,
|
|
234
|
+
maxPriorityFeePerGas,
|
|
235
|
+
estimatedTotalFee,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
this.log.info(`Estimated epoch proof submission cost (not submitted)`, {
|
|
239
|
+
epochNumber,
|
|
240
|
+
fromCheckpoint,
|
|
241
|
+
toCheckpoint,
|
|
242
|
+
gasLimit: gasLimit.toString(),
|
|
243
|
+
baseFeePerGas: formatGwei(baseFeePerGas),
|
|
244
|
+
maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
|
|
245
|
+
estimatedTotalFeeEth: formatEther(estimatedTotalFee),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
this.metrics.recordEstimatedSubmitProof(stats);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private encodeSubmitEpochProofCalldata(args: {
|
|
252
|
+
fromCheckpoint: CheckpointNumber;
|
|
253
|
+
toCheckpoint: CheckpointNumber;
|
|
254
|
+
publicInputs: RootRollupPublicInputs;
|
|
255
|
+
proof: Proof;
|
|
256
|
+
batchedBlobInputs: BatchedBlob;
|
|
257
|
+
attestations: ViemCommitteeAttestation[];
|
|
258
|
+
headers: CheckpointHeader[];
|
|
259
|
+
}): Hex {
|
|
260
|
+
return encodeFunctionData({
|
|
261
|
+
abi: RollupAbi,
|
|
262
|
+
functionName: 'submitEpochRootProof',
|
|
263
|
+
args: [this.getSubmitEpochProofArgs(args)],
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
211
267
|
private async sendSubmitEpochProofTx(args: {
|
|
212
268
|
fromCheckpoint: CheckpointNumber;
|
|
213
269
|
toCheckpoint: CheckpointNumber;
|
|
270
|
+
deadline?: Date;
|
|
214
271
|
publicInputs: RootRollupPublicInputs;
|
|
215
272
|
proof: Proof;
|
|
216
273
|
batchedBlobInputs: BatchedBlob;
|
|
217
274
|
attestations: ViemCommitteeAttestation[];
|
|
275
|
+
headers: CheckpointHeader[];
|
|
218
276
|
}): Promise<TransactionReceipt | undefined> {
|
|
219
277
|
const txArgs = [this.getSubmitEpochProofArgs(args)] as const;
|
|
220
278
|
|
|
@@ -229,7 +287,10 @@ export class ProverNodePublisher {
|
|
|
229
287
|
args: txArgs,
|
|
230
288
|
});
|
|
231
289
|
try {
|
|
232
|
-
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction(
|
|
290
|
+
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction(
|
|
291
|
+
{ to: this.rollupContract.address, data },
|
|
292
|
+
{ txTimeoutAt: args.deadline },
|
|
293
|
+
);
|
|
233
294
|
if (receipt.status !== 'success') {
|
|
234
295
|
const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(
|
|
235
296
|
data,
|
|
@@ -258,6 +319,7 @@ export class ProverNodePublisher {
|
|
|
258
319
|
publicInputs: RootRollupPublicInputs;
|
|
259
320
|
batchedBlobInputs: BatchedBlob;
|
|
260
321
|
attestations: ViemCommitteeAttestation[];
|
|
322
|
+
headers: CheckpointHeader[];
|
|
261
323
|
}) {
|
|
262
324
|
// Returns arguments for EpochProofLib.sol -> getEpochProofPublicInputs()
|
|
263
325
|
return [
|
|
@@ -269,11 +331,7 @@ export class ProverNodePublisher {
|
|
|
269
331
|
outHash: args.publicInputs.outHash.toString(),
|
|
270
332
|
proverId: EthAddress.fromField(args.publicInputs.constants.proverId).toString(),
|
|
271
333
|
} /*_args*/,
|
|
272
|
-
|
|
273
|
-
i % 2 === 0
|
|
274
|
-
? args.publicInputs.fees[i / 2].recipient.toField().toString()
|
|
275
|
-
: args.publicInputs.fees[(i - 1) / 2].value.toString(),
|
|
276
|
-
) /*_fees*/,
|
|
334
|
+
args.headers.map(header => header.toViem()) /*_headers*/,
|
|
277
335
|
getEthBlobEvaluationInputs(args.batchedBlobInputs) /*_blobPublicInputs*/,
|
|
278
336
|
] as const;
|
|
279
337
|
}
|
|
@@ -285,6 +343,7 @@ export class ProverNodePublisher {
|
|
|
285
343
|
proof: Proof;
|
|
286
344
|
batchedBlobInputs: BatchedBlob;
|
|
287
345
|
attestations: ViemCommitteeAttestation[];
|
|
346
|
+
headers: CheckpointHeader[];
|
|
288
347
|
}) {
|
|
289
348
|
// Returns arguments for EpochProofLib.sol -> submitEpochRootProof()
|
|
290
349
|
const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
|
|
@@ -293,12 +352,109 @@ export class ProverNodePublisher {
|
|
|
293
352
|
start: argsArray[0],
|
|
294
353
|
end: argsArray[1],
|
|
295
354
|
args: argsArray[2],
|
|
296
|
-
|
|
297
|
-
attestations:
|
|
355
|
+
headers: argsArray[3],
|
|
356
|
+
attestations: CommitteeAttestationsAndSigners.packAttestations(
|
|
298
357
|
args.attestations.map(a => CommitteeAttestation.fromViem(a)),
|
|
299
|
-
)
|
|
358
|
+
),
|
|
300
359
|
blobInputs: argsArray[4],
|
|
301
360
|
proof: proofHex,
|
|
302
361
|
};
|
|
303
362
|
}
|
|
304
363
|
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Decodes a `Root rollup public inputs mismatch`, fetches the on-chain CheckpointLog for any
|
|
367
|
+
* mismatching `checkpointHeaderHashes[i]`, emits a structured error log, and returns a thrown-ready
|
|
368
|
+
* Error with a human-readable summary.
|
|
369
|
+
*
|
|
370
|
+
* Layout of `RootRollupPublicInputs.toFields()`:
|
|
371
|
+
* [0] previousArchiveRoot
|
|
372
|
+
* [1] endArchiveRoot
|
|
373
|
+
* [2] outHash
|
|
374
|
+
* [3 .. 3+N-1] checkpointHeaderHashes[i] for i in 0..N-1 (N = MAX_CHECKPOINTS_PER_EPOCH)
|
|
375
|
+
* [3+N .. 3+3N-1] fees[i] = (recipient, value) for i in 0..N-1
|
|
376
|
+
* [3+3N .. 3+3N+4] EpochConstantData (chainId, version, vkTreeRoot, protocolContractsHash, proverId)
|
|
377
|
+
* [3+3N+5 ..] blobPublicInputs (FinalBlobAccumulator)
|
|
378
|
+
*/
|
|
379
|
+
async function reportPublicInputsMismatch(input: {
|
|
380
|
+
rollupPublicInputs: readonly Fr[];
|
|
381
|
+
argsPublicInputs: readonly Fr[];
|
|
382
|
+
fromCheckpoint: CheckpointNumber;
|
|
383
|
+
toCheckpoint: CheckpointNumber;
|
|
384
|
+
rollupContract: RollupContract;
|
|
385
|
+
log: Logger;
|
|
386
|
+
}): Promise<Error> {
|
|
387
|
+
const { rollupPublicInputs, argsPublicInputs, fromCheckpoint, toCheckpoint, rollupContract, log } = input;
|
|
388
|
+
const N = MAX_CHECKPOINTS_PER_EPOCH;
|
|
389
|
+
const constantsStart = 3 + 3 * N;
|
|
390
|
+
const blobStart = constantsStart + 5;
|
|
391
|
+
const constantLabels = ['chainId', 'version', 'vkTreeRoot', 'protocolContractsHash', 'proverId'];
|
|
392
|
+
|
|
393
|
+
const diffs: { index: number; label: string; rollup: Fr; computed: Fr; checkpointIndex?: number }[] = [];
|
|
394
|
+
const len = Math.max(rollupPublicInputs.length, argsPublicInputs.length);
|
|
395
|
+
for (let i = 0; i < len; i++) {
|
|
396
|
+
const a = rollupPublicInputs[i] ?? Fr.ZERO;
|
|
397
|
+
const b = argsPublicInputs[i] ?? Fr.ZERO;
|
|
398
|
+
if (a.equals(b)) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
let label: string;
|
|
402
|
+
let checkpointIndex: number | undefined;
|
|
403
|
+
if (i === 0) {
|
|
404
|
+
label = 'previousArchiveRoot';
|
|
405
|
+
} else if (i === 1) {
|
|
406
|
+
label = 'endArchiveRoot';
|
|
407
|
+
} else if (i === 2) {
|
|
408
|
+
label = 'outHash';
|
|
409
|
+
} else if (i < 3 + N) {
|
|
410
|
+
checkpointIndex = i - 3;
|
|
411
|
+
label = `checkpointHeaderHashes[${checkpointIndex}]`;
|
|
412
|
+
} else if (i < 3 + 3 * N) {
|
|
413
|
+
const feePairIndex = i - (3 + N);
|
|
414
|
+
const feeIndex = Math.floor(feePairIndex / 2);
|
|
415
|
+
const sub = feePairIndex % 2 === 0 ? 'recipient' : 'value';
|
|
416
|
+
label = `fees[${feeIndex}].${sub}`;
|
|
417
|
+
} else if (i < blobStart) {
|
|
418
|
+
label = `constants.${constantLabels[i - constantsStart]}`;
|
|
419
|
+
} else {
|
|
420
|
+
label = `blobPublicInputs[${i - blobStart}]`;
|
|
421
|
+
}
|
|
422
|
+
diffs.push({ index: i, label, rollup: a, computed: b, checkpointIndex });
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// For each mismatching checkpointHeaderHash, fetch the L1 CheckpointLog so the operator can
|
|
426
|
+
// see what was published on-chain alongside the prover's recomputed hash.
|
|
427
|
+
const onChainCheckpoints = await Promise.all(
|
|
428
|
+
diffs
|
|
429
|
+
.filter(d => d.checkpointIndex !== undefined)
|
|
430
|
+
.map(async d => {
|
|
431
|
+
const checkpointNumber = CheckpointNumber(fromCheckpoint + d.checkpointIndex!);
|
|
432
|
+
try {
|
|
433
|
+
const cp = await rollupContract.getCheckpoint(checkpointNumber);
|
|
434
|
+
return { checkpointIndex: d.checkpointIndex!, checkpointNumber, headerHash: cp.headerHash.toString() };
|
|
435
|
+
} catch (err) {
|
|
436
|
+
return { checkpointIndex: d.checkpointIndex!, checkpointNumber, error: (err as Error).message };
|
|
437
|
+
}
|
|
438
|
+
}),
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
log.error(`Root rollup public inputs mismatch`, undefined, {
|
|
442
|
+
fromCheckpoint,
|
|
443
|
+
toCheckpoint,
|
|
444
|
+
numDiffs: diffs.length,
|
|
445
|
+
diffs: diffs.map(d => ({
|
|
446
|
+
index: d.index,
|
|
447
|
+
label: d.label,
|
|
448
|
+
rollup: d.rollup.toString(),
|
|
449
|
+
computed: d.computed.toString(),
|
|
450
|
+
})),
|
|
451
|
+
onChainCheckpoints,
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
const fmt = (inputs: readonly Fr[]) => inputs.map(x => x.toString()).join(', ');
|
|
455
|
+
const summary = diffs.map(d => `[${d.index} ${d.label}] L1=${d.rollup} prover=${d.computed}`).join('\n');
|
|
456
|
+
return new Error(
|
|
457
|
+
`Root rollup public inputs mismatch (${diffs.length} fields differ):\n${summary}\n` +
|
|
458
|
+
`Rollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`,
|
|
459
|
+
);
|
|
460
|
+
}
|