@aztec/sequencer-client 0.0.1-commit.d3ec352c → 0.0.1-commit.e61ad554
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/client/sequencer-client.d.ts +12 -12
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +32 -25
- package/dest/config.d.ts +12 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +73 -30
- package/dest/global_variable_builder/global_builder.d.ts +21 -12
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +51 -41
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/config.d.ts +7 -4
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +9 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts +5 -4
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +15 -86
- package/dest/publisher/sequencer-publisher.d.ts +48 -40
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +564 -114
- package/dest/sequencer/checkpoint_proposal_job.d.ts +79 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job.js +1165 -0
- package/dest/sequencer/checkpoint_voter.d.ts +35 -0
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_voter.js +109 -0
- package/dest/sequencer/config.d.ts +3 -2
- package/dest/sequencer/config.d.ts.map +1 -1
- package/dest/sequencer/events.d.ts +46 -0
- package/dest/sequencer/events.d.ts.map +1 -0
- package/dest/sequencer/events.js +1 -0
- package/dest/sequencer/index.d.ts +4 -2
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +3 -1
- package/dest/sequencer/metrics.d.ts +22 -2
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +125 -62
- package/dest/sequencer/sequencer.d.ts +107 -131
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +690 -602
- package/dest/sequencer/timetable.d.ts +54 -14
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +148 -59
- package/dest/sequencer/types.d.ts +3 -0
- package/dest/sequencer/types.d.ts.map +1 -0
- package/dest/sequencer/types.js +1 -0
- package/dest/sequencer/utils.d.ts +14 -8
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +7 -4
- package/dest/test/index.d.ts +4 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +95 -0
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -0
- package/dest/test/mock_checkpoint_builder.js +222 -0
- package/dest/test/utils.d.ts +53 -0
- package/dest/test/utils.d.ts.map +1 -0
- package/dest/test/utils.js +103 -0
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +30 -41
- package/src/config.ts +78 -34
- package/src/global_variable_builder/global_builder.ts +63 -59
- package/src/index.ts +1 -7
- package/src/publisher/config.ts +12 -9
- package/src/publisher/sequencer-publisher-factory.ts +5 -4
- package/src/publisher/sequencer-publisher-metrics.ts +16 -72
- package/src/publisher/sequencer-publisher.ts +262 -155
- package/src/sequencer/README.md +531 -0
- package/src/sequencer/checkpoint_proposal_job.ts +845 -0
- package/src/sequencer/checkpoint_voter.ts +130 -0
- package/src/sequencer/config.ts +2 -1
- package/src/sequencer/events.ts +27 -0
- package/src/sequencer/index.ts +3 -1
- package/src/sequencer/metrics.ts +164 -70
- package/src/sequencer/sequencer.ts +430 -804
- package/src/sequencer/timetable.ts +173 -79
- package/src/sequencer/types.ts +6 -0
- package/src/sequencer/utils.ts +18 -9
- package/src/test/index.ts +3 -2
- package/src/test/mock_checkpoint_builder.ts +311 -0
- package/src/test/utils.ts +164 -0
- package/dest/sequencer/block_builder.d.ts +0 -28
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -134
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- package/src/sequencer/block_builder.ts +0 -222
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -1,44 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
|
-
import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
4
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
|
+
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
5
5
|
import {
|
|
6
6
|
type EmpireSlashingProposerContract,
|
|
7
|
-
FormattedViemError,
|
|
8
7
|
type GovernanceProposerContract,
|
|
9
8
|
type IEmpireBase,
|
|
10
|
-
type L1BlobInputs,
|
|
11
|
-
type L1ContractsConfig,
|
|
12
|
-
type L1TxConfig,
|
|
13
|
-
type L1TxRequest,
|
|
14
9
|
MULTI_CALL_3_ADDRESS,
|
|
15
10
|
Multicall3,
|
|
16
11
|
RollupContract,
|
|
17
12
|
type TallySlashingProposerContract,
|
|
18
|
-
type TransactionStats,
|
|
19
13
|
type ViemCommitteeAttestations,
|
|
20
14
|
type ViemHeader,
|
|
15
|
+
} from '@aztec/ethereum/contracts';
|
|
16
|
+
import { type L1FeeAnalysisResult, L1FeeAnalyzer } from '@aztec/ethereum/l1-fee-analysis';
|
|
17
|
+
import {
|
|
18
|
+
type L1BlobInputs,
|
|
19
|
+
type L1TxConfig,
|
|
20
|
+
type L1TxRequest,
|
|
21
|
+
type TransactionStats,
|
|
21
22
|
WEI_CONST,
|
|
22
|
-
|
|
23
|
-
tryExtractEvent,
|
|
24
|
-
} from '@aztec/ethereum';
|
|
23
|
+
} from '@aztec/ethereum/l1-tx-utils';
|
|
25
24
|
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
25
|
+
import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
26
26
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
27
27
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
28
|
-
import {
|
|
28
|
+
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
29
|
+
import { pick } from '@aztec/foundation/collection';
|
|
30
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
29
31
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
30
32
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
31
|
-
import type { Fr } from '@aztec/foundation/fields';
|
|
32
33
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
33
34
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
34
35
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
35
36
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
36
37
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
37
|
-
import {
|
|
38
|
+
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
39
|
+
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
38
40
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
39
41
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
40
|
-
import type {
|
|
41
|
-
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
42
|
+
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
43
|
+
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
42
44
|
|
|
43
45
|
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
44
46
|
|
|
@@ -78,12 +80,12 @@ type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slas
|
|
|
78
80
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
79
81
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
80
82
|
|
|
81
|
-
export type
|
|
83
|
+
export type InvalidateCheckpointRequest = {
|
|
82
84
|
request: L1TxRequest;
|
|
83
85
|
reason: 'invalid-attestation' | 'insufficient-attestations';
|
|
84
86
|
gasUsed: bigint;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
checkpointNumber: CheckpointNumber;
|
|
88
|
+
forcePendingCheckpointNumber: CheckpointNumber;
|
|
87
89
|
};
|
|
88
90
|
|
|
89
91
|
interface RequestWithExpiry {
|
|
@@ -108,13 +110,18 @@ export class SequencerPublisher {
|
|
|
108
110
|
|
|
109
111
|
protected lastActions: Partial<Record<Action, SlotNumber>> = {};
|
|
110
112
|
|
|
113
|
+
private isPayloadEmptyCache: Map<string, boolean> = new Map<string, boolean>();
|
|
114
|
+
|
|
111
115
|
protected log: Logger;
|
|
112
116
|
protected ethereumSlotDuration: bigint;
|
|
113
117
|
|
|
114
|
-
private
|
|
118
|
+
private blobClient: BlobClientInterface;
|
|
115
119
|
|
|
116
120
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
117
121
|
private proposerAddressForSimulation?: EthAddress;
|
|
122
|
+
|
|
123
|
+
/** L1 fee analyzer for fisherman mode */
|
|
124
|
+
private l1FeeAnalyzer?: L1FeeAnalyzer;
|
|
118
125
|
// @note - with blobs, the below estimate seems too large.
|
|
119
126
|
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
120
127
|
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
@@ -132,13 +139,15 @@ export class SequencerPublisher {
|
|
|
132
139
|
public slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
133
140
|
public slashFactoryContract: SlashFactoryContract;
|
|
134
141
|
|
|
142
|
+
public readonly tracer: Tracer;
|
|
143
|
+
|
|
135
144
|
protected requests: RequestWithExpiry[] = [];
|
|
136
145
|
|
|
137
146
|
constructor(
|
|
138
147
|
private config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
|
|
139
148
|
deps: {
|
|
140
149
|
telemetry?: TelemetryClient;
|
|
141
|
-
|
|
150
|
+
blobClient: BlobClientInterface;
|
|
142
151
|
l1TxUtils: L1TxUtilsWithBlobs;
|
|
143
152
|
rollupContract: RollupContract;
|
|
144
153
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -156,11 +165,11 @@ export class SequencerPublisher {
|
|
|
156
165
|
this.epochCache = deps.epochCache;
|
|
157
166
|
this.lastActions = deps.lastActions;
|
|
158
167
|
|
|
159
|
-
this.
|
|
160
|
-
deps.blobSinkClient ?? createBlobSinkClient(config, { logger: createLogger('sequencer:blob-sink:client') });
|
|
168
|
+
this.blobClient = deps.blobClient;
|
|
161
169
|
|
|
162
170
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
163
171
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
172
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
164
173
|
this.l1TxUtils = deps.l1TxUtils;
|
|
165
174
|
|
|
166
175
|
this.rollupContract = deps.rollupContract;
|
|
@@ -174,6 +183,15 @@ export class SequencerPublisher {
|
|
|
174
183
|
this.slashingProposerContract = newSlashingProposer;
|
|
175
184
|
});
|
|
176
185
|
this.slashFactoryContract = deps.slashFactoryContract;
|
|
186
|
+
|
|
187
|
+
// Initialize L1 fee analyzer for fisherman mode
|
|
188
|
+
if (config.fishermanMode) {
|
|
189
|
+
this.l1FeeAnalyzer = new L1FeeAnalyzer(
|
|
190
|
+
this.l1TxUtils.client,
|
|
191
|
+
deps.dateProvider,
|
|
192
|
+
createLogger('sequencer:publisher:fee-analyzer'),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
177
195
|
}
|
|
178
196
|
|
|
179
197
|
public getRollupContract(): RollupContract {
|
|
@@ -184,6 +202,13 @@ export class SequencerPublisher {
|
|
|
184
202
|
return this.l1TxUtils.getSenderAddress();
|
|
185
203
|
}
|
|
186
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Gets the L1 fee analyzer instance (only available in fisherman mode)
|
|
207
|
+
*/
|
|
208
|
+
public getL1FeeAnalyzer(): L1FeeAnalyzer | undefined {
|
|
209
|
+
return this.l1FeeAnalyzer;
|
|
210
|
+
}
|
|
211
|
+
|
|
187
212
|
/**
|
|
188
213
|
* Sets the proposer address to use for simulations in fisherman mode.
|
|
189
214
|
* @param proposerAddress - The actual proposer's address to use for balance lookups in simulations
|
|
@@ -211,6 +236,62 @@ export class SequencerPublisher {
|
|
|
211
236
|
}
|
|
212
237
|
}
|
|
213
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Analyzes L1 fees for the pending requests without sending them.
|
|
241
|
+
* This is used in fisherman mode to validate fee calculations.
|
|
242
|
+
* @param l2SlotNumber - The L2 slot number for this analysis
|
|
243
|
+
* @param onComplete - Optional callback to invoke when analysis completes (after block is mined)
|
|
244
|
+
* @returns The analysis result (incomplete until block mines), or undefined if no requests
|
|
245
|
+
*/
|
|
246
|
+
public async analyzeL1Fees(
|
|
247
|
+
l2SlotNumber: SlotNumber,
|
|
248
|
+
onComplete?: (analysis: L1FeeAnalysisResult) => void,
|
|
249
|
+
): Promise<L1FeeAnalysisResult | undefined> {
|
|
250
|
+
if (!this.l1FeeAnalyzer) {
|
|
251
|
+
this.log.warn('L1 fee analyzer not available (not in fisherman mode)');
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const requestsToAnalyze = [...this.requests];
|
|
256
|
+
if (requestsToAnalyze.length === 0) {
|
|
257
|
+
this.log.debug('No requests to analyze for L1 fees');
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Extract blob config from requests (if any)
|
|
262
|
+
const blobConfigs = requestsToAnalyze.filter(request => request.blobConfig).map(request => request.blobConfig);
|
|
263
|
+
const blobConfig = blobConfigs[0];
|
|
264
|
+
|
|
265
|
+
// Get gas configs
|
|
266
|
+
const gasConfigs = requestsToAnalyze.filter(request => request.gasConfig).map(request => request.gasConfig);
|
|
267
|
+
const gasLimits = gasConfigs.map(g => g?.gasLimit).filter((g): g is bigint => g !== undefined);
|
|
268
|
+
const gasLimit = gasLimits.length > 0 ? gasLimits.reduce((sum, g) => sum + g, 0n) : 0n;
|
|
269
|
+
|
|
270
|
+
// Get the transaction requests
|
|
271
|
+
const l1Requests = requestsToAnalyze.map(r => r.request);
|
|
272
|
+
|
|
273
|
+
// Start the analysis
|
|
274
|
+
const analysisId = await this.l1FeeAnalyzer.startAnalysis(
|
|
275
|
+
l2SlotNumber,
|
|
276
|
+
gasLimit > 0n ? gasLimit : SequencerPublisher.PROPOSE_GAS_GUESS,
|
|
277
|
+
l1Requests,
|
|
278
|
+
blobConfig,
|
|
279
|
+
onComplete,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
this.log.info('Started L1 fee analysis', {
|
|
283
|
+
analysisId,
|
|
284
|
+
l2SlotNumber: l2SlotNumber.toString(),
|
|
285
|
+
requestCount: requestsToAnalyze.length,
|
|
286
|
+
hasBlobConfig: !!blobConfig,
|
|
287
|
+
gasLimit: gasLimit.toString(),
|
|
288
|
+
actions: requestsToAnalyze.map(r => r.action),
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Return the analysis result (will be incomplete until block mines)
|
|
292
|
+
return this.l1FeeAnalyzer.getAnalysis(analysisId);
|
|
293
|
+
}
|
|
294
|
+
|
|
214
295
|
/**
|
|
215
296
|
* Sends all requests that are still valid.
|
|
216
297
|
* @returns one of:
|
|
@@ -218,10 +299,11 @@ export class SequencerPublisher {
|
|
|
218
299
|
* - a receipt and errorMsg if it failed on L1
|
|
219
300
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
220
301
|
*/
|
|
302
|
+
@trackSpan('SequencerPublisher.sendRequests')
|
|
221
303
|
public async sendRequests() {
|
|
222
304
|
const requestsToProcess = [...this.requests];
|
|
223
305
|
this.requests = [];
|
|
224
|
-
if (this.interrupted) {
|
|
306
|
+
if (this.interrupted || requestsToProcess.length === 0) {
|
|
225
307
|
return undefined;
|
|
226
308
|
}
|
|
227
309
|
const currentL2Slot = this.getCurrentL2Slot();
|
|
@@ -335,17 +417,14 @@ export class SequencerPublisher {
|
|
|
335
417
|
public canProposeAtNextEthBlock(
|
|
336
418
|
tipArchive: Fr,
|
|
337
419
|
msgSender: EthAddress,
|
|
338
|
-
opts: {
|
|
420
|
+
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
339
421
|
) {
|
|
340
422
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
341
423
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
342
424
|
|
|
343
425
|
return this.rollupContract
|
|
344
426
|
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
345
|
-
forcePendingCheckpointNumber:
|
|
346
|
-
opts.forcePendingBlockNumber !== undefined
|
|
347
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
348
|
-
: undefined,
|
|
427
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
349
428
|
})
|
|
350
429
|
.catch(err => {
|
|
351
430
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
@@ -364,10 +443,11 @@ export class SequencerPublisher {
|
|
|
364
443
|
* It will throw if the block header is invalid.
|
|
365
444
|
* @param header - The block header to validate
|
|
366
445
|
*/
|
|
446
|
+
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
367
447
|
public async validateBlockHeader(
|
|
368
448
|
header: CheckpointHeader,
|
|
369
|
-
opts?: {
|
|
370
|
-
) {
|
|
449
|
+
opts?: { forcePendingCheckpointNumber: CheckpointNumber | undefined },
|
|
450
|
+
): Promise<void> {
|
|
371
451
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
372
452
|
|
|
373
453
|
const args = [
|
|
@@ -376,17 +456,13 @@ export class SequencerPublisher {
|
|
|
376
456
|
[], // no signers
|
|
377
457
|
Signature.empty().toViemSignature(),
|
|
378
458
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
379
|
-
header.
|
|
459
|
+
header.blobsHash.toString(),
|
|
380
460
|
flags,
|
|
381
461
|
] as const;
|
|
382
462
|
|
|
383
463
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
384
|
-
const optsForcePendingCheckpointNumber =
|
|
385
|
-
opts?.forcePendingBlockNumber !== undefined
|
|
386
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
387
|
-
: undefined;
|
|
388
464
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
389
|
-
|
|
465
|
+
opts?.forcePendingCheckpointNumber,
|
|
390
466
|
);
|
|
391
467
|
let balance = 0n;
|
|
392
468
|
if (this.config.fishermanMode) {
|
|
@@ -414,77 +490,90 @@ export class SequencerPublisher {
|
|
|
414
490
|
}
|
|
415
491
|
|
|
416
492
|
/**
|
|
417
|
-
* Simulate making a call to invalidate a
|
|
418
|
-
* @param
|
|
493
|
+
* Simulate making a call to invalidate a checkpoint with invalid attestations. Returns undefined if no need to invalidate.
|
|
494
|
+
* @param validationResult - The validation result indicating which checkpoint to invalidate (as returned by the archiver)
|
|
419
495
|
*/
|
|
420
|
-
public async
|
|
421
|
-
validationResult:
|
|
422
|
-
): Promise<
|
|
496
|
+
public async simulateInvalidateCheckpoint(
|
|
497
|
+
validationResult: ValidateCheckpointResult,
|
|
498
|
+
): Promise<InvalidateCheckpointRequest | undefined> {
|
|
423
499
|
if (validationResult.valid) {
|
|
424
500
|
return undefined;
|
|
425
501
|
}
|
|
426
502
|
|
|
427
|
-
const { reason,
|
|
428
|
-
const
|
|
429
|
-
const logData = { ...
|
|
503
|
+
const { reason, checkpoint } = validationResult;
|
|
504
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
505
|
+
const logData = { ...checkpoint, reason };
|
|
430
506
|
|
|
431
|
-
const
|
|
432
|
-
if (
|
|
507
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
508
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
433
509
|
this.log.verbose(
|
|
434
|
-
`Skipping
|
|
435
|
-
{
|
|
510
|
+
`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`,
|
|
511
|
+
{ currentCheckpointNumber, ...logData },
|
|
436
512
|
);
|
|
437
513
|
return undefined;
|
|
438
514
|
}
|
|
439
515
|
|
|
440
|
-
const request = this.
|
|
441
|
-
this.log.debug(`Simulating invalidate
|
|
516
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
517
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
442
518
|
|
|
443
519
|
try {
|
|
444
520
|
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, ErrorsAbi);
|
|
445
|
-
this.log.verbose(`Simulation for invalidate
|
|
521
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} succeeded`, {
|
|
522
|
+
...logData,
|
|
523
|
+
request,
|
|
524
|
+
gasUsed,
|
|
525
|
+
});
|
|
446
526
|
|
|
447
|
-
return {
|
|
527
|
+
return {
|
|
528
|
+
request,
|
|
529
|
+
gasUsed,
|
|
530
|
+
checkpointNumber,
|
|
531
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
532
|
+
reason,
|
|
533
|
+
};
|
|
448
534
|
} catch (err) {
|
|
449
535
|
const viemError = formatViemError(err);
|
|
450
536
|
|
|
451
|
-
// If the error is due to the
|
|
452
|
-
// we can safely ignore it and return undefined so we go ahead with
|
|
537
|
+
// If the error is due to the checkpoint not being in the pending chain, and it was indeed removed by someone else,
|
|
538
|
+
// we can safely ignore it and return undefined so we go ahead with checkpoint building.
|
|
453
539
|
if (viemError.message?.includes('Rollup__BlockNotInPendingChain')) {
|
|
454
540
|
this.log.verbose(
|
|
455
|
-
`Simulation for invalidate
|
|
541
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
456
542
|
{ ...logData, request, error: viemError.message },
|
|
457
543
|
);
|
|
458
|
-
const
|
|
459
|
-
if (
|
|
460
|
-
this.log.verbose(`
|
|
544
|
+
const latestPendingCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
545
|
+
if (latestPendingCheckpointNumber < checkpointNumber) {
|
|
546
|
+
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
461
547
|
return undefined;
|
|
462
548
|
} else {
|
|
463
549
|
this.log.error(
|
|
464
|
-
`Simulation for invalidate ${
|
|
550
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`,
|
|
465
551
|
viemError,
|
|
466
552
|
logData,
|
|
467
553
|
);
|
|
468
|
-
throw new Error(
|
|
469
|
-
|
|
470
|
-
|
|
554
|
+
throw new Error(
|
|
555
|
+
`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`,
|
|
556
|
+
{
|
|
557
|
+
cause: viemError,
|
|
558
|
+
},
|
|
559
|
+
);
|
|
471
560
|
}
|
|
472
561
|
}
|
|
473
562
|
|
|
474
|
-
// Otherwise, throw. We cannot build the next
|
|
475
|
-
this.log.error(`Simulation for invalidate
|
|
476
|
-
throw new Error(`Failed to simulate invalidate
|
|
563
|
+
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
564
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
565
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
477
566
|
}
|
|
478
567
|
}
|
|
479
568
|
|
|
480
|
-
private
|
|
569
|
+
private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
|
|
481
570
|
if (validationResult.valid) {
|
|
482
|
-
throw new Error('Cannot invalidate a valid
|
|
571
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
483
572
|
}
|
|
484
573
|
|
|
485
|
-
const {
|
|
486
|
-
const logData = { ...
|
|
487
|
-
this.log.debug(`
|
|
574
|
+
const { checkpoint, committee, reason } = validationResult;
|
|
575
|
+
const logData = { ...checkpoint, reason };
|
|
576
|
+
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
488
577
|
|
|
489
578
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(
|
|
490
579
|
validationResult.attestations,
|
|
@@ -492,14 +581,14 @@ export class SequencerPublisher {
|
|
|
492
581
|
|
|
493
582
|
if (reason === 'invalid-attestation') {
|
|
494
583
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
495
|
-
|
|
584
|
+
checkpoint.checkpointNumber,
|
|
496
585
|
attestationsAndSigners,
|
|
497
586
|
committee,
|
|
498
587
|
validationResult.invalidIndex,
|
|
499
588
|
);
|
|
500
589
|
} else if (reason === 'insufficient-attestations') {
|
|
501
590
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
502
|
-
|
|
591
|
+
checkpoint.checkpointNumber,
|
|
503
592
|
attestationsAndSigners,
|
|
504
593
|
committee,
|
|
505
594
|
);
|
|
@@ -509,45 +598,39 @@ export class SequencerPublisher {
|
|
|
509
598
|
}
|
|
510
599
|
}
|
|
511
600
|
|
|
512
|
-
/**
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
*
|
|
517
|
-
* @param block - The block to propose
|
|
518
|
-
* @param attestationData - The block's attestation data
|
|
519
|
-
*
|
|
520
|
-
*/
|
|
521
|
-
public async validateBlockForSubmission(
|
|
522
|
-
block: L2Block,
|
|
601
|
+
/** Simulates `propose` to make sure that the checkpoint is valid for submission */
|
|
602
|
+
@trackSpan('SequencerPublisher.validateCheckpointForSubmission')
|
|
603
|
+
public async validateCheckpointForSubmission(
|
|
604
|
+
checkpoint: Checkpoint,
|
|
523
605
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
524
606
|
attestationsAndSignersSignature: Signature,
|
|
525
|
-
options: {
|
|
607
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
526
608
|
): Promise<bigint> {
|
|
527
609
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
528
610
|
|
|
611
|
+
// TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
|
|
529
612
|
// If we have no attestations, we still need to provide the empty attestations
|
|
530
613
|
// so that the committee is recalculated correctly
|
|
531
|
-
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
532
|
-
if (ignoreSignatures) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
const blobFields =
|
|
614
|
+
// const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
615
|
+
// if (ignoreSignatures) {
|
|
616
|
+
// const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
617
|
+
// if (!committee) {
|
|
618
|
+
// this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
619
|
+
// throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
620
|
+
// }
|
|
621
|
+
// attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
622
|
+
// CommitteeAttestation.fromAddress(committeeMember),
|
|
623
|
+
// );
|
|
624
|
+
// }
|
|
625
|
+
|
|
626
|
+
const blobFields = checkpoint.toBlobFields();
|
|
544
627
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
545
628
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
546
629
|
|
|
547
630
|
const args = [
|
|
548
631
|
{
|
|
549
|
-
header:
|
|
550
|
-
archive: toHex(
|
|
632
|
+
header: checkpoint.header.toViem(),
|
|
633
|
+
archive: toHex(checkpoint.archive.root.toBuffer()),
|
|
551
634
|
oracleInput: {
|
|
552
635
|
feeAssetPriceModifier: 0n,
|
|
553
636
|
},
|
|
@@ -585,10 +668,19 @@ export class SequencerPublisher {
|
|
|
585
668
|
const round = await base.computeRound(slotNumber);
|
|
586
669
|
const roundInfo = await base.getRoundInfo(this.rollupContract.address, round);
|
|
587
670
|
|
|
671
|
+
if (roundInfo.quorumReached) {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
|
|
588
675
|
if (roundInfo.lastSignalSlot >= slotNumber) {
|
|
589
676
|
return false;
|
|
590
677
|
}
|
|
591
678
|
|
|
679
|
+
if (await this.isPayloadEmpty(payload)) {
|
|
680
|
+
this.log.warn(`Skipping vote cast for payload with empty code`);
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
|
|
592
684
|
const cachedLastVote = this.lastActions[signalType];
|
|
593
685
|
this.lastActions[signalType] = slotNumber;
|
|
594
686
|
const action = signalType;
|
|
@@ -631,14 +723,14 @@ export class SequencerPublisher {
|
|
|
631
723
|
const logData = { ...result, slotNumber, round, payload: payload.toString() };
|
|
632
724
|
if (!success) {
|
|
633
725
|
this.log.error(
|
|
634
|
-
`Signaling in
|
|
726
|
+
`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} failed`,
|
|
635
727
|
logData,
|
|
636
728
|
);
|
|
637
729
|
this.lastActions[signalType] = cachedLastVote;
|
|
638
730
|
return false;
|
|
639
731
|
} else {
|
|
640
732
|
this.log.info(
|
|
641
|
-
`Signaling in
|
|
733
|
+
`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} succeeded`,
|
|
642
734
|
logData,
|
|
643
735
|
);
|
|
644
736
|
return true;
|
|
@@ -648,6 +740,17 @@ export class SequencerPublisher {
|
|
|
648
740
|
return true;
|
|
649
741
|
}
|
|
650
742
|
|
|
743
|
+
private async isPayloadEmpty(payload: EthAddress): Promise<boolean> {
|
|
744
|
+
const key = payload.toString();
|
|
745
|
+
const cached = this.isPayloadEmptyCache.get(key);
|
|
746
|
+
if (cached) {
|
|
747
|
+
return cached;
|
|
748
|
+
}
|
|
749
|
+
const isEmpty = !(await this.l1TxUtils.getCode(payload));
|
|
750
|
+
this.isPayloadEmptyCache.set(key, isEmpty);
|
|
751
|
+
return isEmpty;
|
|
752
|
+
}
|
|
753
|
+
|
|
651
754
|
/**
|
|
652
755
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
653
756
|
* @param slotNumber - The slot number to cast a signal for.
|
|
@@ -795,27 +898,21 @@ export class SequencerPublisher {
|
|
|
795
898
|
return true;
|
|
796
899
|
}
|
|
797
900
|
|
|
798
|
-
/**
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
* @param block - L2 block to propose.
|
|
802
|
-
* @returns True if the tx has been enqueued, throws otherwise. See #9315
|
|
803
|
-
*/
|
|
804
|
-
public async enqueueProposeL2Block(
|
|
805
|
-
block: L2Block,
|
|
901
|
+
/** Simulates and enqueues a proposal for a checkpoint on L1 */
|
|
902
|
+
public async enqueueProposeCheckpoint(
|
|
903
|
+
checkpoint: Checkpoint,
|
|
806
904
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
807
905
|
attestationsAndSignersSignature: Signature,
|
|
808
|
-
opts: { txTimeoutAt?: Date;
|
|
809
|
-
): Promise<
|
|
810
|
-
const checkpointHeader =
|
|
906
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
907
|
+
): Promise<void> {
|
|
908
|
+
const checkpointHeader = checkpoint.header;
|
|
811
909
|
|
|
812
|
-
const blobFields =
|
|
910
|
+
const blobFields = checkpoint.toBlobFields();
|
|
813
911
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
814
912
|
|
|
815
913
|
const proposeTxArgs = {
|
|
816
914
|
header: checkpointHeader,
|
|
817
|
-
archive:
|
|
818
|
-
body: block.body.toBuffer(),
|
|
915
|
+
archive: checkpoint.archive.root.toBuffer(),
|
|
819
916
|
blobs,
|
|
820
917
|
attestationsAndSigners,
|
|
821
918
|
attestationsAndSignersSignature,
|
|
@@ -829,22 +926,29 @@ export class SequencerPublisher {
|
|
|
829
926
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
830
927
|
// make time consistency checks break.
|
|
831
928
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
832
|
-
ts = await this.
|
|
929
|
+
ts = await this.validateCheckpointForSubmission(
|
|
930
|
+
checkpoint,
|
|
931
|
+
attestationsAndSigners,
|
|
932
|
+
attestationsAndSignersSignature,
|
|
933
|
+
opts,
|
|
934
|
+
);
|
|
833
935
|
} catch (err: any) {
|
|
834
|
-
this.log.error(`
|
|
835
|
-
...
|
|
836
|
-
slotNumber:
|
|
837
|
-
|
|
936
|
+
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
937
|
+
...checkpoint.getStats(),
|
|
938
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
939
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
838
940
|
});
|
|
839
941
|
throw err;
|
|
840
942
|
}
|
|
841
943
|
|
|
842
|
-
this.log.verbose(`Enqueuing
|
|
843
|
-
await this.addProposeTx(
|
|
844
|
-
return true;
|
|
944
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, { ...checkpoint.toCheckpointInfo(), ...opts });
|
|
945
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
845
946
|
}
|
|
846
947
|
|
|
847
|
-
public
|
|
948
|
+
public enqueueInvalidateCheckpoint(
|
|
949
|
+
request: InvalidateCheckpointRequest | undefined,
|
|
950
|
+
opts: { txTimeoutAt?: Date } = {},
|
|
951
|
+
) {
|
|
848
952
|
if (!request) {
|
|
849
953
|
return;
|
|
850
954
|
}
|
|
@@ -852,9 +956,9 @@ export class SequencerPublisher {
|
|
|
852
956
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
853
957
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(request.gasUsed) * 64) / 63)));
|
|
854
958
|
|
|
855
|
-
const { gasUsed,
|
|
856
|
-
const logData = { gasUsed,
|
|
857
|
-
this.log.verbose(`Enqueuing invalidate
|
|
959
|
+
const { gasUsed, checkpointNumber } = request;
|
|
960
|
+
const logData = { gasUsed, checkpointNumber, gasLimit, opts };
|
|
961
|
+
this.log.verbose(`Enqueuing invalidate checkpoint request`, logData);
|
|
858
962
|
this.addRequest({
|
|
859
963
|
action: `invalidate-by-${request.reason}`,
|
|
860
964
|
request: request.request,
|
|
@@ -867,9 +971,9 @@ export class SequencerPublisher {
|
|
|
867
971
|
result.receipt.status === 'success' &&
|
|
868
972
|
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
869
973
|
if (!success) {
|
|
870
|
-
this.log.warn(`Invalidate
|
|
974
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
|
|
871
975
|
} else {
|
|
872
|
-
this.log.info(`Invalidate
|
|
976
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, { ...result, ...logData });
|
|
873
977
|
}
|
|
874
978
|
return !!success;
|
|
875
979
|
},
|
|
@@ -948,7 +1052,7 @@ export class SequencerPublisher {
|
|
|
948
1052
|
private async prepareProposeTx(
|
|
949
1053
|
encodedData: L1ProcessArgs,
|
|
950
1054
|
timestamp: bigint,
|
|
951
|
-
options: {
|
|
1055
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
952
1056
|
) {
|
|
953
1057
|
const kzg = Blob.getViemKzgInstance();
|
|
954
1058
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
@@ -1029,7 +1133,7 @@ export class SequencerPublisher {
|
|
|
1029
1133
|
`0x${string}`,
|
|
1030
1134
|
],
|
|
1031
1135
|
timestamp: bigint,
|
|
1032
|
-
options: {
|
|
1136
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1033
1137
|
) {
|
|
1034
1138
|
const rollupData = encodeFunctionData({
|
|
1035
1139
|
abi: RollupAbi,
|
|
@@ -1038,13 +1142,9 @@ export class SequencerPublisher {
|
|
|
1038
1142
|
});
|
|
1039
1143
|
|
|
1040
1144
|
// override the pending checkpoint number if requested
|
|
1041
|
-
const optsForcePendingCheckpointNumber =
|
|
1042
|
-
options.forcePendingBlockNumber !== undefined
|
|
1043
|
-
? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
|
|
1044
|
-
: undefined;
|
|
1045
1145
|
const forcePendingCheckpointNumberStateDiff = (
|
|
1046
|
-
|
|
1047
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
1146
|
+
options.forcePendingCheckpointNumber !== undefined
|
|
1147
|
+
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1048
1148
|
: []
|
|
1049
1149
|
).flatMap(override => override.stateDiff ?? []);
|
|
1050
1150
|
|
|
@@ -1106,11 +1206,12 @@ export class SequencerPublisher {
|
|
|
1106
1206
|
}
|
|
1107
1207
|
|
|
1108
1208
|
private async addProposeTx(
|
|
1109
|
-
|
|
1209
|
+
checkpoint: Checkpoint,
|
|
1110
1210
|
encodedData: L1ProcessArgs,
|
|
1111
|
-
opts: { txTimeoutAt?: Date;
|
|
1211
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1112
1212
|
timestamp: bigint,
|
|
1113
1213
|
): Promise<void> {
|
|
1214
|
+
const slot = checkpoint.header.slotNumber;
|
|
1114
1215
|
const timer = new Timer();
|
|
1115
1216
|
const kzg = Blob.getViemKzgInstance();
|
|
1116
1217
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(
|
|
@@ -1125,11 +1226,13 @@ export class SequencerPublisher {
|
|
|
1125
1226
|
SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS, // We issue the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
1126
1227
|
);
|
|
1127
1228
|
|
|
1128
|
-
// Send the blobs to the blob
|
|
1129
|
-
// tx fails but it does get mined. We make sure that the blobs are sent to the blob
|
|
1130
|
-
void
|
|
1131
|
-
this.
|
|
1132
|
-
|
|
1229
|
+
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
1230
|
+
// tx fails but it does get mined. We make sure that the blobs are sent to the blob client regardless of the tx outcome.
|
|
1231
|
+
void Promise.resolve().then(() =>
|
|
1232
|
+
this.blobClient.sendBlobsToFilestore(encodedData.blobs).catch(_err => {
|
|
1233
|
+
this.log.error('Failed to send blobs to blob client');
|
|
1234
|
+
}),
|
|
1235
|
+
);
|
|
1133
1236
|
|
|
1134
1237
|
return this.addRequest({
|
|
1135
1238
|
action: 'propose',
|
|
@@ -1137,7 +1240,7 @@ export class SequencerPublisher {
|
|
|
1137
1240
|
to: this.rollupContract.address,
|
|
1138
1241
|
data: rollupData,
|
|
1139
1242
|
},
|
|
1140
|
-
lastValidL2Slot:
|
|
1243
|
+
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
1141
1244
|
gasConfig: { ...opts, gasLimit },
|
|
1142
1245
|
blobConfig: {
|
|
1143
1246
|
blobs: encodedData.blobs.map(b => b.data),
|
|
@@ -1152,11 +1255,12 @@ export class SequencerPublisher {
|
|
|
1152
1255
|
receipt &&
|
|
1153
1256
|
receipt.status === 'success' &&
|
|
1154
1257
|
tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointProposed');
|
|
1258
|
+
|
|
1155
1259
|
if (success) {
|
|
1156
1260
|
const endBlock = receipt.blockNumber;
|
|
1157
1261
|
const inclusionBlocks = Number(endBlock - startBlock);
|
|
1158
1262
|
const { calldataGas, calldataSize, sender } = stats!;
|
|
1159
|
-
const publishStats:
|
|
1263
|
+
const publishStats: L1PublishCheckpointStats = {
|
|
1160
1264
|
gasPrice: receipt.effectiveGasPrice,
|
|
1161
1265
|
gasUsed: receipt.gasUsed,
|
|
1162
1266
|
blobGasUsed: receipt.blobGasUsed ?? 0n,
|
|
@@ -1165,23 +1269,26 @@ export class SequencerPublisher {
|
|
|
1165
1269
|
calldataGas,
|
|
1166
1270
|
calldataSize,
|
|
1167
1271
|
sender,
|
|
1168
|
-
...
|
|
1272
|
+
...checkpoint.getStats(),
|
|
1169
1273
|
eventName: 'rollup-published-to-l1',
|
|
1170
1274
|
blobCount: encodedData.blobs.length,
|
|
1171
1275
|
inclusionBlocks,
|
|
1172
1276
|
};
|
|
1173
|
-
this.log.info(`Published
|
|
1277
|
+
this.log.info(`Published checkpoint ${checkpoint.number} at slot ${slot} to rollup contract`, {
|
|
1278
|
+
...stats,
|
|
1279
|
+
...checkpoint.getStats(),
|
|
1280
|
+
...pick(receipt, 'transactionHash', 'blockHash'),
|
|
1281
|
+
});
|
|
1174
1282
|
this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
|
|
1175
1283
|
|
|
1176
1284
|
return true;
|
|
1177
1285
|
} else {
|
|
1178
1286
|
this.metrics.recordFailedTx('process');
|
|
1179
|
-
this.log.error(
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
});
|
|
1287
|
+
this.log.error(
|
|
1288
|
+
`Publishing checkpoint at slot ${slot} failed with ${errorMsg ?? 'no error message'}`,
|
|
1289
|
+
undefined,
|
|
1290
|
+
{ ...checkpoint.getStats(), ...receipt },
|
|
1291
|
+
);
|
|
1185
1292
|
return false;
|
|
1186
1293
|
}
|
|
1187
1294
|
},
|