@aztec/sequencer-client 0.0.1-commit.6230efd → 0.0.1-commit.6b90f3f5
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 +4 -5
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +1 -1
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -1
- 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-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 +17 -17
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +481 -67
- package/dest/sequencer/checkpoint_proposal_job.d.ts +34 -12
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +605 -55
- 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 +4 -4
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +48 -129
- package/dest/sequencer/sequencer.d.ts +25 -15
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +486 -42
- 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 +26 -17
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +4 -5
- package/src/config.ts +5 -0
- package/src/global_variable_builder/global_builder.ts +13 -13
- package/src/index.ts +1 -9
- package/src/publisher/sequencer-publisher-metrics.ts +17 -69
- package/src/publisher/sequencer-publisher.ts +118 -91
- package/src/sequencer/checkpoint_proposal_job.ts +253 -85
- package/src/sequencer/checkpoint_voter.ts +32 -7
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/metrics.ts +48 -138
- package/src/sequencer/sequencer.ts +125 -42
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +91 -29
- package/src/test/utils.ts +55 -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
|
@@ -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,6 +135,8 @@ 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(
|
|
@@ -167,6 +165,7 @@ export class SequencerPublisher {
|
|
|
167
165
|
|
|
168
166
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
169
167
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
168
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
170
169
|
this.l1TxUtils = deps.l1TxUtils;
|
|
171
170
|
|
|
172
171
|
this.rollupContract = deps.rollupContract;
|
|
@@ -270,7 +269,7 @@ export class SequencerPublisher {
|
|
|
270
269
|
// Start the analysis
|
|
271
270
|
const analysisId = await this.l1FeeAnalyzer.startAnalysis(
|
|
272
271
|
l2SlotNumber,
|
|
273
|
-
gasLimit > 0n ? gasLimit :
|
|
272
|
+
gasLimit > 0n ? gasLimit : MAX_L1_TX_LIMIT,
|
|
274
273
|
l1Requests,
|
|
275
274
|
blobConfig,
|
|
276
275
|
onComplete,
|
|
@@ -296,6 +295,7 @@ export class SequencerPublisher {
|
|
|
296
295
|
* - a receipt and errorMsg if it failed on L1
|
|
297
296
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
298
297
|
*/
|
|
298
|
+
@trackSpan('SequencerPublisher.sendRequests')
|
|
299
299
|
public async sendRequests() {
|
|
300
300
|
const requestsToProcess = [...this.requests];
|
|
301
301
|
this.requests = [];
|
|
@@ -342,7 +342,16 @@ export class SequencerPublisher {
|
|
|
342
342
|
|
|
343
343
|
// Merge gasConfigs. Yields the sum of gasLimits, and the earliest txTimeoutAt, or undefined if no gasConfig sets them.
|
|
344
344
|
const gasLimits = gasConfigs.map(g => g?.gasLimit).filter((g): g is bigint => g !== undefined);
|
|
345
|
-
|
|
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
|
+
}
|
|
346
355
|
const txTimeoutAts = gasConfigs.map(g => g?.txTimeoutAt).filter((g): g is Date => g !== undefined);
|
|
347
356
|
const txTimeoutAt = txTimeoutAts.length > 0 ? new Date(Math.min(...txTimeoutAts.map(g => g.getTime()))) : undefined; // earliest
|
|
348
357
|
const txConfig: RequestWithExpiry['gasConfig'] = { gasLimit, txTimeoutAt };
|
|
@@ -413,17 +422,14 @@ export class SequencerPublisher {
|
|
|
413
422
|
public canProposeAtNextEthBlock(
|
|
414
423
|
tipArchive: Fr,
|
|
415
424
|
msgSender: EthAddress,
|
|
416
|
-
opts: {
|
|
425
|
+
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
417
426
|
) {
|
|
418
427
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
419
428
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
420
429
|
|
|
421
430
|
return this.rollupContract
|
|
422
431
|
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
423
|
-
forcePendingCheckpointNumber:
|
|
424
|
-
opts.forcePendingBlockNumber !== undefined
|
|
425
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
426
|
-
: undefined,
|
|
432
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
427
433
|
})
|
|
428
434
|
.catch(err => {
|
|
429
435
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
@@ -442,10 +448,11 @@ export class SequencerPublisher {
|
|
|
442
448
|
* It will throw if the block header is invalid.
|
|
443
449
|
* @param header - The block header to validate
|
|
444
450
|
*/
|
|
451
|
+
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
445
452
|
public async validateBlockHeader(
|
|
446
453
|
header: CheckpointHeader,
|
|
447
|
-
opts?: {
|
|
448
|
-
) {
|
|
454
|
+
opts?: { forcePendingCheckpointNumber: CheckpointNumber | undefined },
|
|
455
|
+
): Promise<void> {
|
|
449
456
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
450
457
|
|
|
451
458
|
const args = [
|
|
@@ -454,17 +461,13 @@ export class SequencerPublisher {
|
|
|
454
461
|
[], // no signers
|
|
455
462
|
Signature.empty().toViemSignature(),
|
|
456
463
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
457
|
-
header.
|
|
464
|
+
header.blobsHash.toString(),
|
|
458
465
|
flags,
|
|
459
466
|
] as const;
|
|
460
467
|
|
|
461
468
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
462
|
-
const optsForcePendingCheckpointNumber =
|
|
463
|
-
opts?.forcePendingBlockNumber !== undefined
|
|
464
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
465
|
-
: undefined;
|
|
466
469
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
467
|
-
|
|
470
|
+
opts?.forcePendingCheckpointNumber,
|
|
468
471
|
);
|
|
469
472
|
let balance = 0n;
|
|
470
473
|
if (this.config.fishermanMode) {
|
|
@@ -492,77 +495,95 @@ export class SequencerPublisher {
|
|
|
492
495
|
}
|
|
493
496
|
|
|
494
497
|
/**
|
|
495
|
-
* Simulate making a call to invalidate a
|
|
496
|
-
* @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)
|
|
497
500
|
*/
|
|
498
|
-
public async
|
|
499
|
-
validationResult:
|
|
500
|
-
): Promise<
|
|
501
|
+
public async simulateInvalidateCheckpoint(
|
|
502
|
+
validationResult: ValidateCheckpointResult,
|
|
503
|
+
): Promise<InvalidateCheckpointRequest | undefined> {
|
|
501
504
|
if (validationResult.valid) {
|
|
502
505
|
return undefined;
|
|
503
506
|
}
|
|
504
507
|
|
|
505
|
-
const { reason,
|
|
506
|
-
const
|
|
507
|
-
const logData = { ...
|
|
508
|
+
const { reason, checkpoint } = validationResult;
|
|
509
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
510
|
+
const logData = { ...checkpoint, reason };
|
|
508
511
|
|
|
509
|
-
const
|
|
510
|
-
if (
|
|
512
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
513
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
511
514
|
this.log.verbose(
|
|
512
|
-
`Skipping
|
|
513
|
-
{
|
|
515
|
+
`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`,
|
|
516
|
+
{ currentCheckpointNumber, ...logData },
|
|
514
517
|
);
|
|
515
518
|
return undefined;
|
|
516
519
|
}
|
|
517
520
|
|
|
518
|
-
const request = this.
|
|
519
|
-
this.log.debug(`Simulating invalidate
|
|
521
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
522
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
520
523
|
|
|
521
524
|
try {
|
|
522
|
-
const { gasUsed } = await this.l1TxUtils.simulate(
|
|
523
|
-
|
|
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
|
+
});
|
|
524
536
|
|
|
525
|
-
return {
|
|
537
|
+
return {
|
|
538
|
+
request,
|
|
539
|
+
gasUsed,
|
|
540
|
+
checkpointNumber,
|
|
541
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
542
|
+
reason,
|
|
543
|
+
};
|
|
526
544
|
} catch (err) {
|
|
527
545
|
const viemError = formatViemError(err);
|
|
528
546
|
|
|
529
|
-
// If the error is due to the
|
|
530
|
-
// we can safely ignore it and return undefined so we go ahead with
|
|
531
|
-
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')) {
|
|
532
550
|
this.log.verbose(
|
|
533
|
-
`Simulation for invalidate
|
|
551
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
534
552
|
{ ...logData, request, error: viemError.message },
|
|
535
553
|
);
|
|
536
|
-
const
|
|
537
|
-
if (
|
|
538
|
-
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 });
|
|
539
557
|
return undefined;
|
|
540
558
|
} else {
|
|
541
559
|
this.log.error(
|
|
542
|
-
`Simulation for invalidate ${
|
|
560
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`,
|
|
543
561
|
viemError,
|
|
544
562
|
logData,
|
|
545
563
|
);
|
|
546
|
-
throw new Error(
|
|
547
|
-
|
|
548
|
-
|
|
564
|
+
throw new Error(
|
|
565
|
+
`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`,
|
|
566
|
+
{
|
|
567
|
+
cause: viemError,
|
|
568
|
+
},
|
|
569
|
+
);
|
|
549
570
|
}
|
|
550
571
|
}
|
|
551
572
|
|
|
552
|
-
// Otherwise, throw. We cannot build the next
|
|
553
|
-
this.log.error(`Simulation for invalidate
|
|
554
|
-
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 });
|
|
555
576
|
}
|
|
556
577
|
}
|
|
557
578
|
|
|
558
|
-
private
|
|
579
|
+
private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
|
|
559
580
|
if (validationResult.valid) {
|
|
560
|
-
throw new Error('Cannot invalidate a valid
|
|
581
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
561
582
|
}
|
|
562
583
|
|
|
563
|
-
const {
|
|
564
|
-
const logData = { ...
|
|
565
|
-
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);
|
|
566
587
|
|
|
567
588
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(
|
|
568
589
|
validationResult.attestations,
|
|
@@ -570,14 +591,14 @@ export class SequencerPublisher {
|
|
|
570
591
|
|
|
571
592
|
if (reason === 'invalid-attestation') {
|
|
572
593
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
573
|
-
|
|
594
|
+
checkpoint.checkpointNumber,
|
|
574
595
|
attestationsAndSigners,
|
|
575
596
|
committee,
|
|
576
597
|
validationResult.invalidIndex,
|
|
577
598
|
);
|
|
578
599
|
} else if (reason === 'insufficient-attestations') {
|
|
579
600
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
580
|
-
|
|
601
|
+
checkpoint.checkpointNumber,
|
|
581
602
|
attestationsAndSigners,
|
|
582
603
|
committee,
|
|
583
604
|
);
|
|
@@ -588,11 +609,12 @@ export class SequencerPublisher {
|
|
|
588
609
|
}
|
|
589
610
|
|
|
590
611
|
/** Simulates `propose` to make sure that the checkpoint is valid for submission */
|
|
612
|
+
@trackSpan('SequencerPublisher.validateCheckpointForSubmission')
|
|
591
613
|
public async validateCheckpointForSubmission(
|
|
592
614
|
checkpoint: Checkpoint,
|
|
593
615
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
594
616
|
attestationsAndSignersSignature: Signature,
|
|
595
|
-
options: {
|
|
617
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
596
618
|
): Promise<bigint> {
|
|
597
619
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
598
620
|
|
|
@@ -688,7 +710,7 @@ export class SequencerPublisher {
|
|
|
688
710
|
});
|
|
689
711
|
|
|
690
712
|
try {
|
|
691
|
-
await this.l1TxUtils.simulate(request, { time: timestamp }, [], ErrorsAbi);
|
|
713
|
+
await this.l1TxUtils.simulate(request, { time: timestamp }, [], mergeAbis([request.abi ?? [], ErrorsAbi]));
|
|
692
714
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, { request });
|
|
693
715
|
} catch (err) {
|
|
694
716
|
this.log.error(`Failed simulation for ${action} at slot ${slotNumber} (enqueuing the action anyway)`, err);
|
|
@@ -891,7 +913,7 @@ export class SequencerPublisher {
|
|
|
891
913
|
checkpoint: Checkpoint,
|
|
892
914
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
893
915
|
attestationsAndSignersSignature: Signature,
|
|
894
|
-
opts: { txTimeoutAt?: Date;
|
|
916
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
895
917
|
): Promise<void> {
|
|
896
918
|
const checkpointHeader = checkpoint.header;
|
|
897
919
|
|
|
@@ -924,7 +946,7 @@ export class SequencerPublisher {
|
|
|
924
946
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
925
947
|
...checkpoint.getStats(),
|
|
926
948
|
slotNumber: checkpoint.header.slotNumber,
|
|
927
|
-
|
|
949
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
928
950
|
});
|
|
929
951
|
throw err;
|
|
930
952
|
}
|
|
@@ -933,7 +955,10 @@ export class SequencerPublisher {
|
|
|
933
955
|
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
934
956
|
}
|
|
935
957
|
|
|
936
|
-
public
|
|
958
|
+
public enqueueInvalidateCheckpoint(
|
|
959
|
+
request: InvalidateCheckpointRequest | undefined,
|
|
960
|
+
opts: { txTimeoutAt?: Date } = {},
|
|
961
|
+
) {
|
|
937
962
|
if (!request) {
|
|
938
963
|
return;
|
|
939
964
|
}
|
|
@@ -941,9 +966,9 @@ export class SequencerPublisher {
|
|
|
941
966
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
942
967
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(request.gasUsed) * 64) / 63)));
|
|
943
968
|
|
|
944
|
-
const { gasUsed,
|
|
945
|
-
const logData = { gasUsed,
|
|
946
|
-
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);
|
|
947
972
|
this.addRequest({
|
|
948
973
|
action: `invalidate-by-${request.reason}`,
|
|
949
974
|
request: request.request,
|
|
@@ -956,9 +981,9 @@ export class SequencerPublisher {
|
|
|
956
981
|
result.receipt.status === 'success' &&
|
|
957
982
|
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
958
983
|
if (!success) {
|
|
959
|
-
this.log.warn(`Invalidate
|
|
984
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
|
|
960
985
|
} else {
|
|
961
|
-
this.log.info(`Invalidate
|
|
986
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, { ...result, ...logData });
|
|
962
987
|
}
|
|
963
988
|
return !!success;
|
|
964
989
|
},
|
|
@@ -984,12 +1009,14 @@ export class SequencerPublisher {
|
|
|
984
1009
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
985
1010
|
|
|
986
1011
|
let gasUsed: bigint;
|
|
1012
|
+
const simulateAbi = mergeAbis([request.abi ?? [], ErrorsAbi]);
|
|
987
1013
|
try {
|
|
988
|
-
({ 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
|
|
989
1015
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
990
1016
|
} catch (err) {
|
|
991
|
-
const viemError = formatViemError(err);
|
|
1017
|
+
const viemError = formatViemError(err, simulateAbi);
|
|
992
1018
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
1019
|
+
|
|
993
1020
|
return false;
|
|
994
1021
|
}
|
|
995
1022
|
|
|
@@ -997,10 +1024,14 @@ export class SequencerPublisher {
|
|
|
997
1024
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(gasUsed) * 64) / 63)));
|
|
998
1025
|
logData.gasLimit = gasLimit;
|
|
999
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
|
+
|
|
1000
1031
|
this.log.debug(`Enqueuing ${action}`, logData);
|
|
1001
1032
|
this.addRequest({
|
|
1002
1033
|
action,
|
|
1003
|
-
request,
|
|
1034
|
+
request: requestWithAbi,
|
|
1004
1035
|
gasConfig: { gasLimit },
|
|
1005
1036
|
lastValidL2Slot: slotNumber,
|
|
1006
1037
|
checkSuccess: (_req, result) => {
|
|
@@ -1037,7 +1068,7 @@ export class SequencerPublisher {
|
|
|
1037
1068
|
private async prepareProposeTx(
|
|
1038
1069
|
encodedData: L1ProcessArgs,
|
|
1039
1070
|
timestamp: bigint,
|
|
1040
|
-
options: {
|
|
1071
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1041
1072
|
) {
|
|
1042
1073
|
const kzg = Blob.getViemKzgInstance();
|
|
1043
1074
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
@@ -1118,7 +1149,7 @@ export class SequencerPublisher {
|
|
|
1118
1149
|
`0x${string}`,
|
|
1119
1150
|
],
|
|
1120
1151
|
timestamp: bigint,
|
|
1121
|
-
options: {
|
|
1152
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1122
1153
|
) {
|
|
1123
1154
|
const rollupData = encodeFunctionData({
|
|
1124
1155
|
abi: RollupAbi,
|
|
@@ -1127,13 +1158,9 @@ export class SequencerPublisher {
|
|
|
1127
1158
|
});
|
|
1128
1159
|
|
|
1129
1160
|
// override the pending checkpoint number if requested
|
|
1130
|
-
const optsForcePendingCheckpointNumber =
|
|
1131
|
-
options.forcePendingBlockNumber !== undefined
|
|
1132
|
-
? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
|
|
1133
|
-
: undefined;
|
|
1134
1161
|
const forcePendingCheckpointNumberStateDiff = (
|
|
1135
|
-
|
|
1136
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
1162
|
+
options.forcePendingCheckpointNumber !== undefined
|
|
1163
|
+
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1137
1164
|
: []
|
|
1138
1165
|
).flatMap(override => override.stateDiff ?? []);
|
|
1139
1166
|
|
|
@@ -1160,20 +1187,20 @@ export class SequencerPublisher {
|
|
|
1160
1187
|
{
|
|
1161
1188
|
to: this.rollupContract.address,
|
|
1162
1189
|
data: rollupData,
|
|
1163
|
-
gas:
|
|
1190
|
+
gas: MAX_L1_TX_LIMIT,
|
|
1164
1191
|
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1165
1192
|
},
|
|
1166
1193
|
{
|
|
1167
1194
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
1168
1195
|
time: timestamp + 1n,
|
|
1169
1196
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1170
|
-
gasLimit:
|
|
1197
|
+
gasLimit: MAX_L1_TX_LIMIT * 2n,
|
|
1171
1198
|
},
|
|
1172
1199
|
stateOverrides,
|
|
1173
1200
|
RollupAbi,
|
|
1174
1201
|
{
|
|
1175
1202
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
1176
|
-
fallbackGasEstimate:
|
|
1203
|
+
fallbackGasEstimate: MAX_L1_TX_LIMIT,
|
|
1177
1204
|
},
|
|
1178
1205
|
)
|
|
1179
1206
|
.catch(err => {
|
|
@@ -1183,7 +1210,7 @@ export class SequencerPublisher {
|
|
|
1183
1210
|
this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
|
|
1184
1211
|
// Return a minimal simulation result with the fallback gas estimate
|
|
1185
1212
|
return {
|
|
1186
|
-
gasUsed:
|
|
1213
|
+
gasUsed: MAX_L1_TX_LIMIT,
|
|
1187
1214
|
logs: [],
|
|
1188
1215
|
};
|
|
1189
1216
|
}
|
|
@@ -1197,7 +1224,7 @@ export class SequencerPublisher {
|
|
|
1197
1224
|
private async addProposeTx(
|
|
1198
1225
|
checkpoint: Checkpoint,
|
|
1199
1226
|
encodedData: L1ProcessArgs,
|
|
1200
|
-
opts: { txTimeoutAt?: Date;
|
|
1227
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1201
1228
|
timestamp: bigint,
|
|
1202
1229
|
): Promise<void> {
|
|
1203
1230
|
const slot = checkpoint.header.slotNumber;
|