@aztec/sequencer-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.08c5969dc
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 +5 -6
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +1 -1
- package/dest/config.d.ts +1 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -9
- package/dest/global_variable_builder/global_builder.d.ts +4 -4
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +13 -13
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +2 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +23 -86
- package/dest/publisher/sequencer-publisher.d.ts +19 -19
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +482 -71
- package/dest/sequencer/checkpoint_proposal_job.d.ts +40 -12
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +610 -59
- package/dest/sequencer/checkpoint_voter.d.ts +3 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +34 -10
- package/dest/sequencer/index.d.ts +1 -3
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +0 -2
- package/dest/sequencer/metrics.d.ts +7 -4
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +72 -128
- package/dest/sequencer/sequencer.d.ts +27 -15
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +492 -43
- package/dest/sequencer/timetable.d.ts +1 -4
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +1 -4
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +23 -11
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +50 -9
- package/dest/test/utils.d.ts +13 -9
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +27 -17
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +5 -6
- package/src/config.ts +14 -11
- package/src/global_variable_builder/global_builder.ts +13 -13
- package/src/index.ts +1 -9
- package/src/publisher/sequencer-publisher-factory.ts +1 -1
- package/src/publisher/sequencer-publisher-metrics.ts +17 -69
- package/src/publisher/sequencer-publisher.ts +121 -95
- package/src/sequencer/checkpoint_proposal_job.ts +263 -89
- package/src/sequencer/checkpoint_voter.ts +32 -7
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/metrics.ts +70 -136
- package/src/sequencer/sequencer.ts +133 -43
- package/src/sequencer/timetable.ts +6 -5
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +91 -29
- package/src/test/utils.ts +56 -28
- package/dest/sequencer/block_builder.d.ts +0 -26
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -129
- package/dest/sequencer/checkpoint_builder.d.ts +0 -63
- package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
- package/dest/sequencer/checkpoint_builder.js +0 -131
- 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 -217
- package/src/sequencer/checkpoint_builder.ts +0 -217
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
@@ -18,14 +18,15 @@ import {
|
|
|
18
18
|
type L1BlobInputs,
|
|
19
19
|
type L1TxConfig,
|
|
20
20
|
type L1TxRequest,
|
|
21
|
+
MAX_L1_TX_LIMIT,
|
|
21
22
|
type TransactionStats,
|
|
22
23
|
WEI_CONST,
|
|
23
24
|
} from '@aztec/ethereum/l1-tx-utils';
|
|
24
25
|
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
25
|
-
import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
26
|
+
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
26
27
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
27
28
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
28
|
-
import {
|
|
29
|
+
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
29
30
|
import { pick } from '@aztec/foundation/collection';
|
|
30
31
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
31
32
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -35,12 +36,12 @@ import { bufferToHex } from '@aztec/foundation/string';
|
|
|
35
36
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
36
37
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
37
38
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
38
|
-
import { CommitteeAttestationsAndSigners, type
|
|
39
|
+
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
39
40
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
40
41
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
41
42
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
42
43
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
43
|
-
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
44
|
+
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
44
45
|
|
|
45
46
|
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
46
47
|
|
|
@@ -80,12 +81,12 @@ type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slas
|
|
|
80
81
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
81
82
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
82
83
|
|
|
83
|
-
export type
|
|
84
|
+
export type InvalidateCheckpointRequest = {
|
|
84
85
|
request: L1TxRequest;
|
|
85
86
|
reason: 'invalid-attestation' | 'insufficient-attestations';
|
|
86
87
|
gasUsed: bigint;
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
checkpointNumber: CheckpointNumber;
|
|
89
|
+
forcePendingCheckpointNumber: CheckpointNumber;
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
interface RequestWithExpiry {
|
|
@@ -122,11 +123,6 @@ export class SequencerPublisher {
|
|
|
122
123
|
|
|
123
124
|
/** L1 fee analyzer for fisherman mode */
|
|
124
125
|
private l1FeeAnalyzer?: L1FeeAnalyzer;
|
|
125
|
-
// @note - with blobs, the below estimate seems too large.
|
|
126
|
-
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
127
|
-
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
128
|
-
public static PROPOSE_GAS_GUESS: bigint = 12_000_000n;
|
|
129
|
-
|
|
130
126
|
// A CALL to a cold address is 2700 gas
|
|
131
127
|
public static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
132
128
|
|
|
@@ -139,13 +135,15 @@ export class SequencerPublisher {
|
|
|
139
135
|
public slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
140
136
|
public slashFactoryContract: SlashFactoryContract;
|
|
141
137
|
|
|
138
|
+
public readonly tracer: Tracer;
|
|
139
|
+
|
|
142
140
|
protected requests: RequestWithExpiry[] = [];
|
|
143
141
|
|
|
144
142
|
constructor(
|
|
145
143
|
private config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
|
|
146
144
|
deps: {
|
|
147
145
|
telemetry?: TelemetryClient;
|
|
148
|
-
blobClient
|
|
146
|
+
blobClient: BlobClientInterface;
|
|
149
147
|
l1TxUtils: L1TxUtilsWithBlobs;
|
|
150
148
|
rollupContract: RollupContract;
|
|
151
149
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -163,11 +161,11 @@ export class SequencerPublisher {
|
|
|
163
161
|
this.epochCache = deps.epochCache;
|
|
164
162
|
this.lastActions = deps.lastActions;
|
|
165
163
|
|
|
166
|
-
this.blobClient =
|
|
167
|
-
deps.blobClient ?? createBlobClient(config, { logger: createLogger('sequencer:blob-client:client') });
|
|
164
|
+
this.blobClient = deps.blobClient;
|
|
168
165
|
|
|
169
166
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
170
167
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
168
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
171
169
|
this.l1TxUtils = deps.l1TxUtils;
|
|
172
170
|
|
|
173
171
|
this.rollupContract = deps.rollupContract;
|
|
@@ -271,7 +269,7 @@ export class SequencerPublisher {
|
|
|
271
269
|
// Start the analysis
|
|
272
270
|
const analysisId = await this.l1FeeAnalyzer.startAnalysis(
|
|
273
271
|
l2SlotNumber,
|
|
274
|
-
gasLimit > 0n ? gasLimit :
|
|
272
|
+
gasLimit > 0n ? gasLimit : MAX_L1_TX_LIMIT,
|
|
275
273
|
l1Requests,
|
|
276
274
|
blobConfig,
|
|
277
275
|
onComplete,
|
|
@@ -297,6 +295,7 @@ export class SequencerPublisher {
|
|
|
297
295
|
* - a receipt and errorMsg if it failed on L1
|
|
298
296
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
299
297
|
*/
|
|
298
|
+
@trackSpan('SequencerPublisher.sendRequests')
|
|
300
299
|
public async sendRequests() {
|
|
301
300
|
const requestsToProcess = [...this.requests];
|
|
302
301
|
this.requests = [];
|
|
@@ -343,7 +342,16 @@ export class SequencerPublisher {
|
|
|
343
342
|
|
|
344
343
|
// Merge gasConfigs. Yields the sum of gasLimits, and the earliest txTimeoutAt, or undefined if no gasConfig sets them.
|
|
345
344
|
const gasLimits = gasConfigs.map(g => g?.gasLimit).filter((g): g is bigint => g !== undefined);
|
|
346
|
-
|
|
345
|
+
let gasLimit = gasLimits.length > 0 ? sumBigint(gasLimits) : undefined; // sum
|
|
346
|
+
// Cap at L1 block gas limit so the node accepts the tx ("gas limit too high" otherwise).
|
|
347
|
+
const maxGas = MAX_L1_TX_LIMIT;
|
|
348
|
+
if (gasLimit !== undefined && gasLimit > maxGas) {
|
|
349
|
+
this.log.debug('Capping bundled tx gas limit to L1 max', {
|
|
350
|
+
requested: gasLimit,
|
|
351
|
+
capped: maxGas,
|
|
352
|
+
});
|
|
353
|
+
gasLimit = maxGas;
|
|
354
|
+
}
|
|
347
355
|
const txTimeoutAts = gasConfigs.map(g => g?.txTimeoutAt).filter((g): g is Date => g !== undefined);
|
|
348
356
|
const txTimeoutAt = txTimeoutAts.length > 0 ? new Date(Math.min(...txTimeoutAts.map(g => g.getTime()))) : undefined; // earliest
|
|
349
357
|
const txConfig: RequestWithExpiry['gasConfig'] = { gasLimit, txTimeoutAt };
|
|
@@ -414,17 +422,14 @@ export class SequencerPublisher {
|
|
|
414
422
|
public canProposeAtNextEthBlock(
|
|
415
423
|
tipArchive: Fr,
|
|
416
424
|
msgSender: EthAddress,
|
|
417
|
-
opts: {
|
|
425
|
+
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
418
426
|
) {
|
|
419
427
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
420
428
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
421
429
|
|
|
422
430
|
return this.rollupContract
|
|
423
431
|
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
424
|
-
forcePendingCheckpointNumber:
|
|
425
|
-
opts.forcePendingBlockNumber !== undefined
|
|
426
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
427
|
-
: undefined,
|
|
432
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
428
433
|
})
|
|
429
434
|
.catch(err => {
|
|
430
435
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
@@ -443,10 +448,11 @@ export class SequencerPublisher {
|
|
|
443
448
|
* It will throw if the block header is invalid.
|
|
444
449
|
* @param header - The block header to validate
|
|
445
450
|
*/
|
|
451
|
+
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
446
452
|
public async validateBlockHeader(
|
|
447
453
|
header: CheckpointHeader,
|
|
448
|
-
opts?: {
|
|
449
|
-
) {
|
|
454
|
+
opts?: { forcePendingCheckpointNumber: CheckpointNumber | undefined },
|
|
455
|
+
): Promise<void> {
|
|
450
456
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
451
457
|
|
|
452
458
|
const args = [
|
|
@@ -455,17 +461,13 @@ export class SequencerPublisher {
|
|
|
455
461
|
[], // no signers
|
|
456
462
|
Signature.empty().toViemSignature(),
|
|
457
463
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
458
|
-
header.
|
|
464
|
+
header.blobsHash.toString(),
|
|
459
465
|
flags,
|
|
460
466
|
] as const;
|
|
461
467
|
|
|
462
468
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
463
|
-
const optsForcePendingCheckpointNumber =
|
|
464
|
-
opts?.forcePendingBlockNumber !== undefined
|
|
465
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
466
|
-
: undefined;
|
|
467
469
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
468
|
-
|
|
470
|
+
opts?.forcePendingCheckpointNumber,
|
|
469
471
|
);
|
|
470
472
|
let balance = 0n;
|
|
471
473
|
if (this.config.fishermanMode) {
|
|
@@ -493,77 +495,95 @@ export class SequencerPublisher {
|
|
|
493
495
|
}
|
|
494
496
|
|
|
495
497
|
/**
|
|
496
|
-
* Simulate making a call to invalidate a
|
|
497
|
-
* @param
|
|
498
|
+
* Simulate making a call to invalidate a checkpoint with invalid attestations. Returns undefined if no need to invalidate.
|
|
499
|
+
* @param validationResult - The validation result indicating which checkpoint to invalidate (as returned by the archiver)
|
|
498
500
|
*/
|
|
499
|
-
public async
|
|
500
|
-
validationResult:
|
|
501
|
-
): Promise<
|
|
501
|
+
public async simulateInvalidateCheckpoint(
|
|
502
|
+
validationResult: ValidateCheckpointResult,
|
|
503
|
+
): Promise<InvalidateCheckpointRequest | undefined> {
|
|
502
504
|
if (validationResult.valid) {
|
|
503
505
|
return undefined;
|
|
504
506
|
}
|
|
505
507
|
|
|
506
|
-
const { reason,
|
|
507
|
-
const
|
|
508
|
-
const logData = { ...
|
|
508
|
+
const { reason, checkpoint } = validationResult;
|
|
509
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
510
|
+
const logData = { ...checkpoint, reason };
|
|
509
511
|
|
|
510
|
-
const
|
|
511
|
-
if (
|
|
512
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
513
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
512
514
|
this.log.verbose(
|
|
513
|
-
`Skipping
|
|
514
|
-
{
|
|
515
|
+
`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`,
|
|
516
|
+
{ currentCheckpointNumber, ...logData },
|
|
515
517
|
);
|
|
516
518
|
return undefined;
|
|
517
519
|
}
|
|
518
520
|
|
|
519
|
-
const request = this.
|
|
520
|
-
this.log.debug(`Simulating invalidate
|
|
521
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
522
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
521
523
|
|
|
522
524
|
try {
|
|
523
|
-
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
524
|
-
|
|
525
|
+
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
526
|
+
request,
|
|
527
|
+
undefined,
|
|
528
|
+
undefined,
|
|
529
|
+
mergeAbis([request.abi ?? [], ErrorsAbi]),
|
|
530
|
+
);
|
|
531
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} succeeded`, {
|
|
532
|
+
...logData,
|
|
533
|
+
request,
|
|
534
|
+
gasUsed,
|
|
535
|
+
});
|
|
525
536
|
|
|
526
|
-
return {
|
|
537
|
+
return {
|
|
538
|
+
request,
|
|
539
|
+
gasUsed,
|
|
540
|
+
checkpointNumber,
|
|
541
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
542
|
+
reason,
|
|
543
|
+
};
|
|
527
544
|
} catch (err) {
|
|
528
545
|
const viemError = formatViemError(err);
|
|
529
546
|
|
|
530
|
-
// If the error is due to the
|
|
531
|
-
// we can safely ignore it and return undefined so we go ahead with
|
|
532
|
-
if (viemError.message?.includes('
|
|
547
|
+
// If the error is due to the checkpoint not being in the pending chain, and it was indeed removed by someone else,
|
|
548
|
+
// we can safely ignore it and return undefined so we go ahead with checkpoint building.
|
|
549
|
+
if (viemError.message?.includes('Rollup__CheckpointNotInPendingChain')) {
|
|
533
550
|
this.log.verbose(
|
|
534
|
-
`Simulation for invalidate
|
|
551
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
535
552
|
{ ...logData, request, error: viemError.message },
|
|
536
553
|
);
|
|
537
|
-
const
|
|
538
|
-
if (
|
|
539
|
-
this.log.verbose(`
|
|
554
|
+
const latestPendingCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
555
|
+
if (latestPendingCheckpointNumber < checkpointNumber) {
|
|
556
|
+
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
540
557
|
return undefined;
|
|
541
558
|
} else {
|
|
542
559
|
this.log.error(
|
|
543
|
-
`Simulation for invalidate ${
|
|
560
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`,
|
|
544
561
|
viemError,
|
|
545
562
|
logData,
|
|
546
563
|
);
|
|
547
|
-
throw new Error(
|
|
548
|
-
|
|
549
|
-
|
|
564
|
+
throw new Error(
|
|
565
|
+
`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`,
|
|
566
|
+
{
|
|
567
|
+
cause: viemError,
|
|
568
|
+
},
|
|
569
|
+
);
|
|
550
570
|
}
|
|
551
571
|
}
|
|
552
572
|
|
|
553
|
-
// Otherwise, throw. We cannot build the next
|
|
554
|
-
this.log.error(`Simulation for invalidate
|
|
555
|
-
throw new Error(`Failed to simulate invalidate
|
|
573
|
+
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
574
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
575
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
556
576
|
}
|
|
557
577
|
}
|
|
558
578
|
|
|
559
|
-
private
|
|
579
|
+
private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
|
|
560
580
|
if (validationResult.valid) {
|
|
561
|
-
throw new Error('Cannot invalidate a valid
|
|
581
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
562
582
|
}
|
|
563
583
|
|
|
564
|
-
const {
|
|
565
|
-
const logData = { ...
|
|
566
|
-
this.log.debug(`
|
|
584
|
+
const { checkpoint, committee, reason } = validationResult;
|
|
585
|
+
const logData = { ...checkpoint, reason };
|
|
586
|
+
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
567
587
|
|
|
568
588
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(
|
|
569
589
|
validationResult.attestations,
|
|
@@ -571,14 +591,14 @@ export class SequencerPublisher {
|
|
|
571
591
|
|
|
572
592
|
if (reason === 'invalid-attestation') {
|
|
573
593
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
574
|
-
|
|
594
|
+
checkpoint.checkpointNumber,
|
|
575
595
|
attestationsAndSigners,
|
|
576
596
|
committee,
|
|
577
597
|
validationResult.invalidIndex,
|
|
578
598
|
);
|
|
579
599
|
} else if (reason === 'insufficient-attestations') {
|
|
580
600
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
581
|
-
|
|
601
|
+
checkpoint.checkpointNumber,
|
|
582
602
|
attestationsAndSigners,
|
|
583
603
|
committee,
|
|
584
604
|
);
|
|
@@ -589,11 +609,12 @@ export class SequencerPublisher {
|
|
|
589
609
|
}
|
|
590
610
|
|
|
591
611
|
/** Simulates `propose` to make sure that the checkpoint is valid for submission */
|
|
612
|
+
@trackSpan('SequencerPublisher.validateCheckpointForSubmission')
|
|
592
613
|
public async validateCheckpointForSubmission(
|
|
593
614
|
checkpoint: Checkpoint,
|
|
594
615
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
595
616
|
attestationsAndSignersSignature: Signature,
|
|
596
|
-
options: {
|
|
617
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
597
618
|
): Promise<bigint> {
|
|
598
619
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
599
620
|
|
|
@@ -689,7 +710,7 @@ export class SequencerPublisher {
|
|
|
689
710
|
});
|
|
690
711
|
|
|
691
712
|
try {
|
|
692
|
-
await this.l1TxUtils.simulate(request, { time: timestamp }, [], ErrorsAbi);
|
|
713
|
+
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
693
714
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
694
715
|
} catch (err) {
|
|
695
716
|
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, err);
|
|
@@ -892,7 +913,7 @@ export class SequencerPublisher {
|
|
|
892
913
|
checkpoint: Checkpoint,
|
|
893
914
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
894
915
|
attestationsAndSignersSignature: Signature,
|
|
895
|
-
opts: { txTimeoutAt?: Date;
|
|
916
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
896
917
|
): Promise<void> {
|
|
897
918
|
const checkpointHeader = checkpoint.header;
|
|
898
919
|
|
|
@@ -925,7 +946,7 @@ export class SequencerPublisher {
|
|
|
925
946
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
926
947
|
...checkpoint.getStats(),
|
|
927
948
|
slotNumber: checkpoint.header.slotNumber,
|
|
928
|
-
|
|
949
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
929
950
|
});
|
|
930
951
|
throw err;
|
|
931
952
|
}
|
|
@@ -934,7 +955,10 @@ export class SequencerPublisher {
|
|
|
934
955
|
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
935
956
|
}
|
|
936
957
|
|
|
937
|
-
public
|
|
958
|
+
public enqueueInvalidateCheckpoint(
|
|
959
|
+
request: InvalidateCheckpointRequest | undefined,
|
|
960
|
+
opts: { txTimeoutAt?: Date } = {},
|
|
961
|
+
) {
|
|
938
962
|
if (!request) {
|
|
939
963
|
return;
|
|
940
964
|
}
|
|
@@ -942,9 +966,9 @@ export class SequencerPublisher {
|
|
|
942
966
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
943
967
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(request.gasUsed) * 64) / 63)));
|
|
944
968
|
|
|
945
|
-
const { gasUsed,
|
|
946
|
-
const logData = { gasUsed,
|
|
947
|
-
this.log.verbose(`Enqueuing invalidate
|
|
969
|
+
const { gasUsed, checkpointNumber } = request;
|
|
970
|
+
const logData = { gasUsed, checkpointNumber, gasLimit, opts };
|
|
971
|
+
this.log.verbose(`Enqueuing invalidate checkpoint request`, logData);
|
|
948
972
|
this.addRequest({
|
|
949
973
|
action: `invalidate-by-${request.reason}`,
|
|
950
974
|
request: request.request,
|
|
@@ -957,9 +981,9 @@ export class SequencerPublisher {
|
|
|
957
981
|
result.receipt.status === 'success' &&
|
|
958
982
|
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
959
983
|
if (!success) {
|
|
960
|
-
this.log.warn(`Invalidate
|
|
984
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
|
|
961
985
|
} else {
|
|
962
|
-
this.log.info(`Invalidate
|
|
986
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, { ...result, ...logData });
|
|
963
987
|
}
|
|
964
988
|
return !!success;
|
|
965
989
|
},
|
|
@@ -985,12 +1009,14 @@ export class SequencerPublisher {
|
|
|
985
1009
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
986
1010
|
|
|
987
1011
|
let gasUsed: bigint;
|
|
1012
|
+
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
988
1013
|
try {
|
|
989
|
-
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [],
|
|
1014
|
+
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], simulateAbi)); // TODO(palla/slash): Check the timestamp logic
|
|
990
1015
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
991
1016
|
} catch (err) {
|
|
992
|
-
const viemError = formatViemError(err);
|
|
1017
|
+
const viemError = formatViemError(err, simulateAbi);
|
|
993
1018
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1019
|
+
|
|
994
1020
|
return false;
|
|
995
1021
|
}
|
|
996
1022
|
|
|
@@ -998,10 +1024,14 @@ export class SequencerPublisher {
|
|
|
998
1024
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(gasUsed) * 64) / 63)));
|
|
999
1025
|
logData.gasLimit = gasLimit;
|
|
1000
1026
|
|
|
1027
|
+
// Store the ABI used for simulation on the request so Multicall3.forward can decode errors
|
|
1028
|
+
// when the tx is sent and a revert is diagnosed via simulation.
|
|
1029
|
+
const requestWithAbi = { ...request, abi: simulateAbi };
|
|
1030
|
+
|
|
1001
1031
|
this.log.debug(`Enqueuing ${action}`, logData);
|
|
1002
1032
|
this.addRequest({
|
|
1003
1033
|
action,
|
|
1004
|
-
request,
|
|
1034
|
+
request: requestWithAbi,
|
|
1005
1035
|
gasConfig: { gasLimit },
|
|
1006
1036
|
lastValidL2Slot: slotNumber,
|
|
1007
1037
|
checkSuccess: (_req, result) => {
|
|
@@ -1038,7 +1068,7 @@ export class SequencerPublisher {
|
|
|
1038
1068
|
private async prepareProposeTx(
|
|
1039
1069
|
encodedData: L1ProcessArgs,
|
|
1040
1070
|
timestamp: bigint,
|
|
1041
|
-
options: {
|
|
1071
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1042
1072
|
) {
|
|
1043
1073
|
const kzg = Blob.getViemKzgInstance();
|
|
1044
1074
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
@@ -1119,7 +1149,7 @@ export class SequencerPublisher {
|
|
|
1119
1149
|
`0x${string}`,
|
|
1120
1150
|
],
|
|
1121
1151
|
timestamp: bigint,
|
|
1122
|
-
options: {
|
|
1152
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1123
1153
|
) {
|
|
1124
1154
|
const rollupData = encodeFunctionData({
|
|
1125
1155
|
abi: RollupAbi,
|
|
@@ -1128,13 +1158,9 @@ export class SequencerPublisher {
|
|
|
1128
1158
|
});
|
|
1129
1159
|
|
|
1130
1160
|
// override the pending checkpoint number if requested
|
|
1131
|
-
const optsForcePendingCheckpointNumber =
|
|
1132
|
-
options.forcePendingBlockNumber !== undefined
|
|
1133
|
-
? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
|
|
1134
|
-
: undefined;
|
|
1135
1161
|
const forcePendingCheckpointNumberStateDiff = (
|
|
1136
|
-
|
|
1137
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
1162
|
+
options.forcePendingCheckpointNumber !== undefined
|
|
1163
|
+
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1138
1164
|
: []
|
|
1139
1165
|
).flatMap(override => override.stateDiff ?? []);
|
|
1140
1166
|
|
|
@@ -1161,20 +1187,20 @@ export class SequencerPublisher {
|
|
|
1161
1187
|
{
|
|
1162
1188
|
to: this.rollupContract.address,
|
|
1163
1189
|
data: rollupData,
|
|
1164
|
-
gas:
|
|
1190
|
+
gas: MAX_L1_TX_LIMIT,
|
|
1165
1191
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1166
1192
|
},
|
|
1167
1193
|
{
|
|
1168
1194
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
1169
1195
|
time: timestamp + 1n,
|
|
1170
1196
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1171
|
-
gasLimit:
|
|
1197
|
+
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1172
1198
|
},
|
|
1173
1199
|
stateOverrides,
|
|
1174
1200
|
RollupAbi,
|
|
1175
1201
|
{
|
|
1176
1202
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
1177
|
-
fallbackGasEstimate:
|
|
1203
|
+
fallbackGasEstimate: MAX_L1_TX_LIMIT,
|
|
1178
1204
|
},
|
|
1179
1205
|
)
|
|
1180
1206
|
.catch(err => {
|
|
@@ -1184,7 +1210,7 @@ export class SequencerPublisher {
|
|
|
1184
1210
|
this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
|
|
1185
1211
|
// Return a minimal simulation result with the fallback gas estimate
|
|
1186
1212
|
return {
|
|
1187
|
-
gasUsed:
|
|
1213
|
+
gasUsed: MAX_L1_TX_LIMIT,
|
|
1188
1214
|
logs: [],
|
|
1189
1215
|
};
|
|
1190
1216
|
}
|
|
@@ -1198,7 +1224,7 @@ export class SequencerPublisher {
|
|
|
1198
1224
|
private async addProposeTx(
|
|
1199
1225
|
checkpoint: Checkpoint,
|
|
1200
1226
|
encodedData: L1ProcessArgs,
|
|
1201
|
-
opts: { txTimeoutAt?: Date;
|
|
1227
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1202
1228
|
timestamp: bigint,
|
|
1203
1229
|
): Promise<void> {
|
|
1204
1230
|
const slot = checkpoint.header.slotNumber;
|