@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.
Files changed (79) hide show
  1. package/dest/client/sequencer-client.d.ts +5 -6
  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 -2
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +10 -9
  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-factory.d.ts +2 -2
  14. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  15. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  16. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-metrics.js +23 -86
  18. package/dest/publisher/sequencer-publisher.d.ts +19 -19
  19. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  20. package/dest/publisher/sequencer-publisher.js +482 -71
  21. package/dest/sequencer/checkpoint_proposal_job.d.ts +40 -12
  22. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  23. package/dest/sequencer/checkpoint_proposal_job.js +610 -59
  24. package/dest/sequencer/checkpoint_voter.d.ts +3 -2
  25. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  26. package/dest/sequencer/checkpoint_voter.js +34 -10
  27. package/dest/sequencer/index.d.ts +1 -3
  28. package/dest/sequencer/index.d.ts.map +1 -1
  29. package/dest/sequencer/index.js +0 -2
  30. package/dest/sequencer/metrics.d.ts +7 -4
  31. package/dest/sequencer/metrics.d.ts.map +1 -1
  32. package/dest/sequencer/metrics.js +72 -128
  33. package/dest/sequencer/sequencer.d.ts +27 -15
  34. package/dest/sequencer/sequencer.d.ts.map +1 -1
  35. package/dest/sequencer/sequencer.js +492 -43
  36. package/dest/sequencer/timetable.d.ts +1 -4
  37. package/dest/sequencer/timetable.d.ts.map +1 -1
  38. package/dest/sequencer/timetable.js +1 -4
  39. package/dest/test/index.d.ts +2 -3
  40. package/dest/test/index.d.ts.map +1 -1
  41. package/dest/test/mock_checkpoint_builder.d.ts +23 -11
  42. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  43. package/dest/test/mock_checkpoint_builder.js +50 -9
  44. package/dest/test/utils.d.ts +13 -9
  45. package/dest/test/utils.d.ts.map +1 -1
  46. package/dest/test/utils.js +27 -17
  47. package/package.json +30 -28
  48. package/src/client/sequencer-client.ts +5 -6
  49. package/src/config.ts +14 -11
  50. package/src/global_variable_builder/global_builder.ts +13 -13
  51. package/src/index.ts +1 -9
  52. package/src/publisher/sequencer-publisher-factory.ts +1 -1
  53. package/src/publisher/sequencer-publisher-metrics.ts +17 -69
  54. package/src/publisher/sequencer-publisher.ts +121 -95
  55. package/src/sequencer/checkpoint_proposal_job.ts +263 -89
  56. package/src/sequencer/checkpoint_voter.ts +32 -7
  57. package/src/sequencer/index.ts +0 -2
  58. package/src/sequencer/metrics.ts +70 -136
  59. package/src/sequencer/sequencer.ts +133 -43
  60. package/src/sequencer/timetable.ts +6 -5
  61. package/src/test/index.ts +1 -2
  62. package/src/test/mock_checkpoint_builder.ts +91 -29
  63. package/src/test/utils.ts +56 -28
  64. package/dest/sequencer/block_builder.d.ts +0 -26
  65. package/dest/sequencer/block_builder.d.ts.map +0 -1
  66. package/dest/sequencer/block_builder.js +0 -129
  67. package/dest/sequencer/checkpoint_builder.d.ts +0 -63
  68. package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
  69. package/dest/sequencer/checkpoint_builder.js +0 -131
  70. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  71. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  72. package/dest/tx_validator/nullifier_cache.js +0 -24
  73. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  74. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  75. package/dest/tx_validator/tx_validator_factory.js +0 -53
  76. package/src/sequencer/block_builder.ts +0 -217
  77. package/src/sequencer/checkpoint_builder.ts +0 -217
  78. package/src/tx_validator/nullifier_cache.ts +0 -30
  79. package/src/tx_validator/tx_validator_factory.ts +0 -133
@@ -1,4 +1,4 @@
1
- import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
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 { 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,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?: BlobClientInterface;
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 : SequencerPublisher.PROPOSE_GAS_GUESS,
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
- 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
+ }
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: { forcePendingBlockNumber?: BlockNumber } = {},
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?: { forcePendingBlockNumber: BlockNumber | undefined },
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.contentCommitment.blobsHash.toString(),
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
- optsForcePendingCheckpointNumber,
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 block with invalid attestations. Returns undefined if no need to invalidate.
497
- * @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)
498
500
  */
499
- public async simulateInvalidateBlock(
500
- validationResult: ValidateBlockResult,
501
- ): Promise<InvalidateBlockRequest | undefined> {
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, block } = validationResult;
507
- const blockNumber = block.blockNumber;
508
- const logData = { ...block, reason };
508
+ const { reason, checkpoint } = validationResult;
509
+ const checkpointNumber = checkpoint.checkpointNumber;
510
+ const logData = { ...checkpoint, reason };
509
511
 
510
- const currentBlockNumber = await this.rollupContract.getCheckpointNumber();
511
- if (currentBlockNumber < validationResult.block.blockNumber) {
512
+ const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
513
+ if (currentCheckpointNumber < checkpointNumber) {
512
514
  this.log.verbose(
513
- `Skipping block ${blockNumber} invalidation since it has already been removed from the pending chain`,
514
- { currentBlockNumber, ...logData },
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.buildInvalidateBlockRequest(validationResult);
520
- 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 });
521
523
 
522
524
  try {
523
- const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, ErrorsAbi);
524
- 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
+ });
525
536
 
526
- 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
+ };
527
544
  } catch (err) {
528
545
  const viemError = formatViemError(err);
529
546
 
530
- // If the error is due to the block not being in the pending chain, and it was indeed removed by someone else,
531
- // we can safely ignore it and return undefined so we go ahead with block building.
532
- 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')) {
533
550
  this.log.verbose(
534
- `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`,
535
552
  { ...logData, request, error: viemError.message },
536
553
  );
537
- const latestPendingBlockNumber = await this.rollupContract.getCheckpointNumber();
538
- if (latestPendingBlockNumber < blockNumber) {
539
- 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 });
540
557
  return undefined;
541
558
  } else {
542
559
  this.log.error(
543
- `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`,
544
561
  viemError,
545
562
  logData,
546
563
  );
547
- throw new Error(`Failed to simulate invalidate block ${blockNumber} while it is still in pending chain`, {
548
- cause: viemError,
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 block if we cannot invalidate the previous one.
554
- this.log.error(`Simulation for invalidate block ${blockNumber} failed`, viemError, logData);
555
- 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 });
556
576
  }
557
577
  }
558
578
 
559
- private buildInvalidateBlockRequest(validationResult: ValidateBlockResult) {
579
+ private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
560
580
  if (validationResult.valid) {
561
- throw new Error('Cannot invalidate a valid block');
581
+ throw new Error('Cannot invalidate a valid checkpoint');
562
582
  }
563
583
 
564
- const { block, committee, reason } = validationResult;
565
- const logData = { ...block, reason };
566
- 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);
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
- CheckpointNumber.fromBlockNumber(block.blockNumber),
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
- CheckpointNumber.fromBlockNumber(block.blockNumber),
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: { forcePendingBlockNumber?: BlockNumber }, // TODO(palla/mbps): Should this be forcePendingCheckpointNumber?
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; forcePendingBlockNumber?: BlockNumber } = {},
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
- forcePendingBlockNumber: opts.forcePendingBlockNumber,
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 enqueueInvalidateBlock(request: InvalidateBlockRequest | undefined, opts: { txTimeoutAt?: Date } = {}) {
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, blockNumber } = request;
946
- const logData = { gasUsed, blockNumber, gasLimit, opts };
947
- 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);
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 block ${request.blockNumber} failed`, { ...result, ...logData });
984
+ this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
961
985
  } else {
962
- this.log.info(`Invalidate block ${request.blockNumber} succeeded`, { ...result, ...logData });
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 }, [], 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
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: { forcePendingBlockNumber?: BlockNumber },
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: { forcePendingBlockNumber?: BlockNumber },
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
- optsForcePendingCheckpointNumber !== undefined
1137
- ? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber)
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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: SequencerPublisher.PROPOSE_GAS_GUESS * 2n,
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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: SequencerPublisher.PROPOSE_GAS_GUESS,
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; forcePendingBlockNumber?: BlockNumber } = {},
1227
+ opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
1202
1228
  timestamp: bigint,
1203
1229
  ): Promise<void> {
1204
1230
  const slot = checkpoint.header.slotNumber;