@aztec/sequencer-client 0.0.1-commit.6230efd → 0.0.1-commit.6a729f7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -5
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +1 -1
  4. package/dest/config.d.ts +1 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +6 -1
  7. package/dest/global_variable_builder/global_builder.d.ts +4 -4
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +13 -13
  10. package/dest/index.d.ts +2 -3
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -2
  13. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  14. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  15. package/dest/publisher/sequencer-publisher-metrics.js +23 -86
  16. package/dest/publisher/sequencer-publisher.d.ts +17 -17
  17. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  18. package/dest/publisher/sequencer-publisher.js +481 -67
  19. package/dest/sequencer/checkpoint_proposal_job.d.ts +34 -12
  20. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  21. package/dest/sequencer/checkpoint_proposal_job.js +605 -55
  22. package/dest/sequencer/checkpoint_voter.d.ts +3 -2
  23. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  24. package/dest/sequencer/checkpoint_voter.js +34 -10
  25. package/dest/sequencer/index.d.ts +1 -3
  26. package/dest/sequencer/index.d.ts.map +1 -1
  27. package/dest/sequencer/index.js +0 -2
  28. package/dest/sequencer/metrics.d.ts +4 -4
  29. package/dest/sequencer/metrics.d.ts.map +1 -1
  30. package/dest/sequencer/metrics.js +48 -129
  31. package/dest/sequencer/sequencer.d.ts +25 -15
  32. package/dest/sequencer/sequencer.d.ts.map +1 -1
  33. package/dest/sequencer/sequencer.js +486 -42
  34. package/dest/test/index.d.ts +2 -3
  35. package/dest/test/index.d.ts.map +1 -1
  36. package/dest/test/mock_checkpoint_builder.d.ts +23 -11
  37. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  38. package/dest/test/mock_checkpoint_builder.js +50 -9
  39. package/dest/test/utils.d.ts +13 -9
  40. package/dest/test/utils.d.ts.map +1 -1
  41. package/dest/test/utils.js +26 -17
  42. package/package.json +30 -28
  43. package/src/client/sequencer-client.ts +4 -5
  44. package/src/config.ts +5 -0
  45. package/src/global_variable_builder/global_builder.ts +13 -13
  46. package/src/index.ts +1 -9
  47. package/src/publisher/sequencer-publisher-metrics.ts +17 -69
  48. package/src/publisher/sequencer-publisher.ts +118 -91
  49. package/src/sequencer/checkpoint_proposal_job.ts +253 -85
  50. package/src/sequencer/checkpoint_voter.ts +32 -7
  51. package/src/sequencer/index.ts +0 -2
  52. package/src/sequencer/metrics.ts +48 -138
  53. package/src/sequencer/sequencer.ts +125 -42
  54. package/src/test/index.ts +1 -2
  55. package/src/test/mock_checkpoint_builder.ts +91 -29
  56. package/src/test/utils.ts +55 -28
  57. package/dest/sequencer/block_builder.d.ts +0 -26
  58. package/dest/sequencer/block_builder.d.ts.map +0 -1
  59. package/dest/sequencer/block_builder.js +0 -129
  60. package/dest/sequencer/checkpoint_builder.d.ts +0 -63
  61. package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
  62. package/dest/sequencer/checkpoint_builder.js +0 -131
  63. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  64. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  65. package/dest/tx_validator/nullifier_cache.js +0 -24
  66. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  67. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  68. package/dest/tx_validator/tx_validator_factory.js +0 -53
  69. package/src/sequencer/block_builder.ts +0 -217
  70. package/src/sequencer/checkpoint_builder.ts +0 -217
  71. package/src/tx_validator/nullifier_cache.ts +0 -30
  72. 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 { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
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 ValidateBlockResult } from '@aztec/stdlib/block';
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 InvalidateBlockRequest = {
84
+ export type InvalidateCheckpointRequest = {
84
85
  request: L1TxRequest;
85
86
  reason: 'invalid-attestation' | 'insufficient-attestations';
86
87
  gasUsed: bigint;
87
- blockNumber: BlockNumber;
88
- forcePendingBlockNumber: BlockNumber;
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 : SequencerPublisher.PROPOSE_GAS_GUESS,
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
- const gasLimit = gasLimits.length > 0 ? sumBigint(gasLimits) : undefined; // sum
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: { forcePendingBlockNumber?: BlockNumber } = {},
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?: { forcePendingBlockNumber: BlockNumber | undefined },
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.contentCommitment.blobsHash.toString(),
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
- optsForcePendingCheckpointNumber,
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 block with invalid attestations. Returns undefined if no need to invalidate.
496
- * @param block - The block to invalidate and the criteria for invalidation (as returned by the archiver)
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 simulateInvalidateBlock(
499
- validationResult: ValidateBlockResult,
500
- ): Promise<InvalidateBlockRequest | undefined> {
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, block } = validationResult;
506
- const blockNumber = block.blockNumber;
507
- const logData = { ...block, reason };
508
+ const { reason, checkpoint } = validationResult;
509
+ const checkpointNumber = checkpoint.checkpointNumber;
510
+ const logData = { ...checkpoint, reason };
508
511
 
509
- const currentBlockNumber = await this.rollupContract.getCheckpointNumber();
510
- if (currentBlockNumber < validationResult.block.blockNumber) {
512
+ const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
513
+ if (currentCheckpointNumber < checkpointNumber) {
511
514
  this.log.verbose(
512
- `Skipping block ${blockNumber} invalidation since it has already been removed from the pending chain`,
513
- { currentBlockNumber, ...logData },
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.buildInvalidateBlockRequest(validationResult);
519
- this.log.debug(`Simulating invalidate block ${blockNumber}`, { ...logData, request });
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(request, undefined, undefined, ErrorsAbi);
523
- this.log.verbose(`Simulation for invalidate block ${blockNumber} succeeded`, { ...logData, request, gasUsed });
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 { request, gasUsed, blockNumber, forcePendingBlockNumber: BlockNumber(blockNumber - 1), reason };
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 block not being in the pending chain, and it was indeed removed by someone else,
530
- // we can safely ignore it and return undefined so we go ahead with block building.
531
- if (viemError.message?.includes('Rollup__BlockNotInPendingChain')) {
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 block ${blockNumber} failed due to block not being in pending chain`,
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 latestPendingBlockNumber = await this.rollupContract.getCheckpointNumber();
537
- if (latestPendingBlockNumber < blockNumber) {
538
- this.log.verbose(`Block number ${blockNumber} has already been invalidated`, { ...logData });
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 ${blockNumber} failed and it is still in pending chain`,
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(`Failed to simulate invalidate block ${blockNumber} while it is still in pending chain`, {
547
- cause: viemError,
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 block if we cannot invalidate the previous one.
553
- this.log.error(`Simulation for invalidate block ${blockNumber} failed`, viemError, logData);
554
- throw new Error(`Failed to simulate invalidate block ${blockNumber}`, { cause: viemError });
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 buildInvalidateBlockRequest(validationResult: ValidateBlockResult) {
579
+ private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
559
580
  if (validationResult.valid) {
560
- throw new Error('Cannot invalidate a valid block');
581
+ throw new Error('Cannot invalidate a valid checkpoint');
561
582
  }
562
583
 
563
- const { block, committee, reason } = validationResult;
564
- const logData = { ...block, reason };
565
- this.log.debug(`Simulating invalidate block ${block.blockNumber}`, logData);
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
- CheckpointNumber.fromBlockNumber(block.blockNumber),
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
- CheckpointNumber.fromBlockNumber(block.blockNumber),
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: { forcePendingBlockNumber?: BlockNumber }, // TODO(palla/mbps): Should this be forcePendingCheckpointNumber?
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; forcePendingBlockNumber?: BlockNumber } = {},
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
- forcePendingBlockNumber: opts.forcePendingBlockNumber,
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 enqueueInvalidateBlock(request: InvalidateBlockRequest | undefined, opts: { txTimeoutAt?: Date } = {}) {
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, blockNumber } = request;
945
- const logData = { gasUsed, blockNumber, gasLimit, opts };
946
- this.log.verbose(`Enqueuing invalidate block request`, logData);
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 block ${request.blockNumber} failed`, { ...result, ...logData });
984
+ this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
960
985
  } else {
961
- this.log.info(`Invalidate block ${request.blockNumber} succeeded`, { ...result, ...logData });
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 }, [], ErrorsAbi)); // TODO(palla/slash): Check the timestamp logic
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: { forcePendingBlockNumber?: BlockNumber },
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: { forcePendingBlockNumber?: BlockNumber },
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
- optsForcePendingCheckpointNumber !== undefined
1136
- ? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber)
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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: SequencerPublisher.PROPOSE_GAS_GUESS * 2n,
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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; forcePendingBlockNumber?: BlockNumber } = {},
1227
+ opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
1201
1228
  timestamp: bigint,
1202
1229
  ): Promise<void> {
1203
1230
  const slot = checkpoint.header.slotNumber;