@aztec/sequencer-client 0.0.1-commit.f146247c → 0.0.1-commit.f1b29a41e

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 (84) hide show
  1. package/dest/client/sequencer-client.d.ts +15 -7
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +60 -26
  4. package/dest/config.d.ts +26 -7
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +47 -28
  7. package/dest/global_variable_builder/global_builder.d.ts +15 -11
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +29 -25
  10. package/dest/global_variable_builder/index.d.ts +2 -2
  11. package/dest/global_variable_builder/index.d.ts.map +1 -1
  12. package/dest/publisher/config.d.ts +47 -17
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +121 -42
  15. package/dest/publisher/index.d.ts +2 -1
  16. package/dest/publisher/index.d.ts.map +1 -1
  17. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  18. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  19. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  20. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  21. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  22. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  23. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  24. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  25. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  26. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  27. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  28. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  29. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
  30. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  31. package/dest/publisher/sequencer-publisher-factory.js +27 -2
  32. package/dest/publisher/sequencer-publisher.d.ts +76 -30
  33. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  34. package/dest/publisher/sequencer-publisher.js +396 -71
  35. package/dest/sequencer/checkpoint_proposal_job.d.ts +39 -8
  36. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  37. package/dest/sequencer/checkpoint_proposal_job.js +368 -196
  38. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  39. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  40. package/dest/sequencer/checkpoint_voter.js +2 -5
  41. package/dest/sequencer/events.d.ts +2 -1
  42. package/dest/sequencer/events.d.ts.map +1 -1
  43. package/dest/sequencer/metrics.d.ts +21 -5
  44. package/dest/sequencer/metrics.d.ts.map +1 -1
  45. package/dest/sequencer/metrics.js +97 -15
  46. package/dest/sequencer/sequencer.d.ts +42 -17
  47. package/dest/sequencer/sequencer.d.ts.map +1 -1
  48. package/dest/sequencer/sequencer.js +147 -89
  49. package/dest/sequencer/timetable.d.ts +4 -6
  50. package/dest/sequencer/timetable.d.ts.map +1 -1
  51. package/dest/sequencer/timetable.js +7 -11
  52. package/dest/sequencer/types.d.ts +2 -2
  53. package/dest/sequencer/types.d.ts.map +1 -1
  54. package/dest/test/index.d.ts +3 -5
  55. package/dest/test/index.d.ts.map +1 -1
  56. package/dest/test/mock_checkpoint_builder.d.ts +11 -11
  57. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  58. package/dest/test/mock_checkpoint_builder.js +45 -34
  59. package/dest/test/utils.d.ts +3 -3
  60. package/dest/test/utils.d.ts.map +1 -1
  61. package/dest/test/utils.js +5 -4
  62. package/package.json +27 -28
  63. package/src/client/sequencer-client.ts +76 -23
  64. package/src/config.ts +65 -38
  65. package/src/global_variable_builder/global_builder.ts +38 -27
  66. package/src/global_variable_builder/index.ts +1 -1
  67. package/src/publisher/config.ts +153 -43
  68. package/src/publisher/index.ts +3 -0
  69. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  70. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  71. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  72. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  73. package/src/publisher/sequencer-publisher-factory.ts +38 -6
  74. package/src/publisher/sequencer-publisher.ts +442 -95
  75. package/src/sequencer/checkpoint_proposal_job.ts +481 -202
  76. package/src/sequencer/checkpoint_voter.ts +1 -12
  77. package/src/sequencer/events.ts +1 -1
  78. package/src/sequencer/metrics.ts +106 -18
  79. package/src/sequencer/sequencer.ts +212 -105
  80. package/src/sequencer/timetable.ts +13 -12
  81. package/src/sequencer/types.ts +1 -1
  82. package/src/test/index.ts +2 -4
  83. package/src/test/mock_checkpoint_builder.ts +63 -49
  84. package/src/test/utils.ts +5 -2
@@ -380,7 +380,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
380
380
  import { EthAddress } from '@aztec/foundation/eth-address';
381
381
  import { createLogger } from '@aztec/foundation/log';
382
382
  import { RunningPromise } from '@aztec/foundation/running-promise';
383
- import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
383
+ import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
384
384
  import { SequencerConfigSchema } from '@aztec/stdlib/interfaces/server';
385
385
  import { pickFromSchema } from '@aztec/stdlib/schemas';
386
386
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -450,17 +450,12 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
450
450
  state;
451
451
  metrics;
452
452
  /** The last slot for which we attempted to perform our voting duties with degraded block production */ lastSlotForFallbackVote;
453
+ /** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
453
454
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
454
455
  /** Last successful checkpoint proposed */ lastCheckpointProposed;
455
456
  /** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
457
+ /** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */ lastCheckpointProposalJob;
456
458
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
457
- // This shouldn't be here as this gets re-created each time we build/propose a block.
458
- // But we have a number of tests that abuse/rely on this class having a permanent publisher.
459
- // As long as those tests only configure a single publisher they will continue to work.
460
- // This will get re-assigned every time the sequencer goes to build a new block to a publisher that is valid
461
- // for the block proposer.
462
- // TODO(palla/mbps): Remove this field and fix tests
463
- publisher;
464
459
  /** Config for the sequencer */ config;
465
460
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
466
461
  super(), this.publisherFactory = publisherFactory, this.validatorClient = validatorClient, this.globalsBuilder = globalsBuilder, this.p2pClient = p2pClient, this.worldState = worldState, this.slasherClient = slasherClient, this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.checkpointsBuilder = checkpointsBuilder, this.l1Constants = l1Constants, this.dateProvider = dateProvider, this.epochCache = epochCache, this.rollupContract = rollupContract, this.telemetry = telemetry, this.log = log, this.state = (_initProto(this), SequencerState.STOPPED), this.config = DefaultSequencerConfig;
@@ -473,7 +468,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
473
468
  }
474
469
  /** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
475
470
  const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
476
- this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowList'));
471
+ this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
477
472
  this.config = merge(this.config, filteredConfig);
478
473
  this.timetable = new SequencerTimetable({
479
474
  ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
@@ -484,9 +479,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
484
479
  enforce: this.config.enforceTimeTable
485
480
  }, this.metrics, this.log);
486
481
  }
487
- /** Initializes the sequencer (precomputes tables and creates a publisher). Takes about 3s. */ async init() {
482
+ /** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
488
483
  getKzg();
489
- this.publisher = (await this.publisherFactory.create(undefined)).publisher;
490
484
  }
491
485
  /** Starts the sequencer and moves to IDLE state. */ start() {
492
486
  this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
@@ -501,8 +495,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
501
495
  this.setState(SequencerState.STOPPING, undefined, {
502
496
  force: true
503
497
  });
504
- this.publisher?.interrupt();
498
+ await this.publisherFactory.stopAll();
505
499
  await this.runningPromise?.stop();
500
+ await this.lastCheckpointProposalJob?.awaitPendingSubmission();
506
501
  this.setState(SequencerState.STOPPED, undefined, {
507
502
  force: true
508
503
  });
@@ -516,7 +511,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
516
511
  error: err
517
512
  });
518
513
  if (err instanceof SequencerTooSlowError) {
519
- // TODO(palla/mbps): Add missing states
520
514
  // Log as warn only if we had to abort halfway through the block proposal
521
515
  const logLvl = [
522
516
  SequencerState.INITIALIZING_CHECKPOINT,
@@ -547,22 +541,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
547
541
  * - Submit checkpoint
548
542
  */ async work() {
549
543
  this.setState(SequencerState.SYNCHRONIZING, undefined);
550
- const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
544
+ const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
545
+ const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
551
546
  // Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
552
- const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
547
+ const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
553
548
  if (!checkpointProposalJob) {
554
549
  return;
555
550
  }
551
+ // Track the job so we can await its pending L1 submission during shutdown
552
+ this.lastCheckpointProposalJob = checkpointProposalJob;
556
553
  // Execute the checkpoint proposal job
557
554
  const checkpoint = await checkpointProposalJob.execute();
558
555
  // Update last checkpoint proposed (currently unused)
559
556
  if (checkpoint) {
560
557
  this.lastCheckpointProposed = checkpoint;
561
558
  }
562
- // Log fee strategy comparison if on fisherman
563
- if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || epoch > this.lastEpochForStrategyComparison)) {
564
- this.logStrategyComparison(epoch, checkpointProposalJob.getPublisher());
565
- this.lastEpochForStrategyComparison = epoch;
559
+ // Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
560
+ if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)) {
561
+ this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
562
+ this.lastEpochForStrategyComparison = targetEpoch;
566
563
  }
567
564
  return checkpoint;
568
565
  }
@@ -570,17 +567,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
570
567
  * Prepares the checkpoint proposal by performing all necessary checks and setup.
571
568
  * This is the initial step in the main loop.
572
569
  * @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
573
- */ async prepareCheckpointProposal(epoch, slot, ts, now) {
574
- // Check we have not already processed this slot (cheapest check)
570
+ */ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
571
+ // Check we have not already processed this target slot (cheapest check)
575
572
  // We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
576
573
  // running against actual time (eg when we use sandbox-style automining)
577
- if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= slot && this.config.enforceTimeTable) {
578
- this.log.trace(`Slot ${slot} has already been processed`);
574
+ if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
575
+ this.log.trace(`Target slot ${targetSlot} has already been processed`);
579
576
  return undefined;
580
577
  }
581
- // But if we have already proposed for this slot, the we definitely have to skip it, automining or not
582
- if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= slot) {
583
- this.log.trace(`Slot ${slot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
578
+ // But if we have already proposed for this slot, then we definitely have to skip it, automining or not
579
+ if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
580
+ this.log.trace(`Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
584
581
  return undefined;
585
582
  }
586
583
  // Check all components are synced to latest as seen by the archiver (queries all subsystems)
@@ -591,16 +588,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
591
588
  if (!syncedTo) {
592
589
  await this.tryVoteWhenSyncFails({
593
590
  slot,
591
+ targetSlot,
594
592
  ts
595
593
  });
596
594
  return undefined;
597
595
  }
598
- // If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
596
+ // If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
599
597
  // Still perform governance/slashing voting (as proposer) once per slot.
600
- const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
598
+ // When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
599
+ const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
601
600
  if (isEscapeHatchOpen) {
602
601
  this.setState(SequencerState.PROPOSER_CHECK, slot);
603
- const [canPropose, proposer] = await this.checkCanPropose(slot);
602
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
604
603
  if (canPropose) {
605
604
  await this.tryVoteWhenEscapeHatchOpen({
606
605
  slot,
@@ -617,30 +616,37 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
617
616
  }
618
617
  // Next checkpoint follows from the last synced one
619
618
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
619
+ // Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
620
+ // confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
621
+ const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
622
+ if (checkpointNumber > confirmedCkpt + 2) {
623
+ this.log.warn(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
624
+ return undefined;
625
+ }
620
626
  const logCtx = {
621
- now,
622
- syncedToL1Ts: syncedTo.l1Timestamp,
623
- syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
627
+ nowSeconds,
628
+ syncedToL2Slot: syncedTo.syncedL2Slot,
624
629
  slot,
630
+ targetSlot,
625
631
  slotTs: ts,
626
632
  checkpointNumber,
627
633
  isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
628
634
  };
629
- // Check that we are a proposer for the next slot
635
+ // Check that we are a proposer for the target slot.
630
636
  this.setState(SequencerState.PROPOSER_CHECK, slot);
631
- const [canPropose, proposer] = await this.checkCanPropose(slot);
637
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
632
638
  // If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
633
639
  if (!canPropose) {
634
640
  await this.considerInvalidatingCheckpoint(syncedTo, slot);
635
641
  return undefined;
636
642
  }
637
- // Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
638
- if (syncedTo.block && syncedTo.block.header.getSlot() >= slot) {
639
- this.log.warn(`Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`, {
643
+ // Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
644
+ if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
645
+ this.log.warn(`Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`, {
640
646
  ...logCtx,
641
- block: syncedTo.block.header.toInspect()
647
+ block: syncedTo.blockData.header.toInspect()
642
648
  });
643
- this.metrics.recordBlockProposalPrecheckFailed('slot_already_taken');
649
+ this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
644
650
  return undefined;
645
651
  }
646
652
  // We now need to get ourselves a publisher.
@@ -650,37 +656,61 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
650
656
  const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
651
657
  const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
652
658
  this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
653
- this.publisher = publisher;
654
659
  // In fisherman mode, set the actual proposer's address for simulations
655
660
  if (this.config.fishermanMode && proposer) {
656
661
  publisher.setProposerAddressForSimulation(proposer);
657
662
  this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
658
663
  }
659
664
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
660
- const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
661
- // Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
662
- // if all the previous checks are good, but we do it just in case.
663
- const canProposeCheck = await publisher.canProposeAtNextEthBlock(syncedTo.archive, proposer ?? EthAddress.ZERO, invalidateCheckpoint);
665
+ let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
666
+ // Determine the correct archive and L1 state overrides for the canProposeAt check.
667
+ // The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
668
+ // When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
669
+ let archiveForCheck = syncedTo.archive;
670
+ const l1Overrides = {};
671
+ if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
672
+ // Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
673
+ // and the archive at that checkpoint so L1 simulation sees the correct chain tip.
674
+ const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
675
+ l1Overrides.forcePendingCheckpointNumber = parentCheckpointNumber;
676
+ l1Overrides.forceArchive = {
677
+ checkpointNumber: parentCheckpointNumber,
678
+ archive: syncedTo.archive
679
+ };
680
+ this.metrics.recordPipelineDepth(1);
681
+ this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
682
+ // Clear the invalidation - the proposed checkpoint should handle it.
683
+ invalidateCheckpoint = undefined;
684
+ } else if (invalidateCheckpoint) {
685
+ // After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
686
+ // exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
687
+ archiveForCheck = invalidateCheckpoint.lastArchive;
688
+ l1Overrides.forcePendingCheckpointNumber = invalidateCheckpoint.forcePendingCheckpointNumber;
689
+ this.metrics.recordPipelineDepth(0);
690
+ } else {
691
+ this.metrics.recordPipelineDepth(0);
692
+ }
693
+ const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, l1Overrides);
664
694
  if (canProposeCheck === undefined) {
665
695
  this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
666
696
  this.emit('proposer-rollup-check-failed', {
667
697
  reason: 'Rollup contract check failed',
668
698
  slot
669
699
  });
670
- this.metrics.recordBlockProposalPrecheckFailed('rollup_contract_check_failed');
700
+ this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
671
701
  return undefined;
672
702
  }
673
- if (canProposeCheck.slot !== slot) {
674
- this.log.warn(`Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${slot} but got ${canProposeCheck.slot}.`, {
703
+ if (canProposeCheck.slot !== targetSlot) {
704
+ this.log.warn(`Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${targetSlot} but got ${canProposeCheck.slot}.`, {
675
705
  ...logCtx,
676
706
  rollup: canProposeCheck,
677
- expectedSlot: slot
707
+ expectedSlot: targetSlot
678
708
  });
679
709
  this.emit('proposer-rollup-check-failed', {
680
710
  reason: 'Slot mismatch',
681
711
  slot
682
712
  });
683
- this.metrics.recordBlockProposalPrecheckFailed('slot_mismatch');
713
+ this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
684
714
  return undefined;
685
715
  }
686
716
  if (canProposeCheck.checkpointNumber !== checkpointNumber) {
@@ -693,19 +723,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
693
723
  reason: 'Block mismatch',
694
724
  slot
695
725
  });
696
- this.metrics.recordBlockProposalPrecheckFailed('block_number_mismatch');
726
+ this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
697
727
  return undefined;
698
728
  }
699
- this.lastSlotForCheckpointProposalJob = slot;
700
- this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, {
729
+ this.lastSlotForCheckpointProposalJob = targetSlot;
730
+ await this.p2pClient.prepareForSlot(targetSlot);
731
+ this.log.info(`Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`, {
701
732
  ...logCtx,
702
- proposer
733
+ proposer,
734
+ pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
703
735
  });
704
736
  // Create and return the checkpoint proposal job
705
- return this.createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint);
737
+ return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
706
738
  }
707
- createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint) {
708
- return new CheckpointProposalJob(epoch, slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, this.validatorClient, this.globalsBuilder, this.p2pClient, this.worldState, this.l1ToL2MessageSource, this.l2BlockSource, this.checkpointsBuilder, this.l2BlockSource, this.l1Constants, this.config, this.timetable, this.slasherClient, this.epochCache, this.dateProvider, this.metrics, this, this.setState.bind(this), this.tracer, this.log.getBindings());
739
+ createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
740
+ return new CheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, this.validatorClient, this.globalsBuilder, this.p2pClient, this.worldState, this.l1ToL2MessageSource, this.l2BlockSource, this.checkpointsBuilder, this.l2BlockSource, this.l1Constants, this.config, this.timetable, this.slasherClient, this.epochCache, this.dateProvider, this.metrics, this, this.setState.bind(this), this.tracer, this.log.getBindings(), proposedCheckpointData);
741
+ }
742
+ /**
743
+ * Returns the current sequencer state.
744
+ */ getState() {
745
+ return this.state;
709
746
  }
710
747
  /**
711
748
  * Internal helper for setting the sequencer state and checks if we have enough time left in the slot to transition to the new state.
@@ -747,16 +784,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
747
784
  * Returns whether all dependencies have caught up.
748
785
  * We don't check against the previous block submitted since it may have been reorg'd out.
749
786
  */ async checkSync(args) {
750
- // Check that the archiver and dependencies have synced to the previous L1 slot at least
751
- // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
752
- // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
753
- const l1Timestamp = await this.l2BlockSource.getL1Timestamp();
754
- const { slot, ts } = args;
755
- if (l1Timestamp === undefined || l1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration) < ts) {
787
+ // Check that the archiver has fully synced the L2 slot before the one we want to propose in.
788
+ // The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
789
+ // See getSyncedL2SlotNumber for how missed L1 blocks are handled.
790
+ const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
791
+ const { slot } = args;
792
+ if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
756
793
  this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
757
794
  slot,
758
- ts,
759
- l1Timestamp
795
+ syncedL2Slot
760
796
  });
761
797
  return undefined;
762
798
  }
@@ -765,20 +801,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
765
801
  number: syncSummary.latestBlockNumber,
766
802
  hash: syncSummary.latestBlockHash
767
803
  })),
768
- this.l2BlockSource.getL2Tips().then((t)=>t.proposed),
804
+ this.l2BlockSource.getL2Tips().then((t)=>({
805
+ proposed: t.proposed,
806
+ checkpointed: t.checkpointed,
807
+ proposedCheckpoint: t.proposedCheckpoint
808
+ })),
769
809
  this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
770
810
  this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
771
- this.l2BlockSource.getPendingChainValidationStatus()
811
+ this.l2BlockSource.getPendingChainValidationStatus(),
812
+ this.l2BlockSource.getProposedCheckpointOnly()
772
813
  ]);
773
- const [worldState, l2BlockSource, p2p, l1ToL2MessageSource, pendingChainValidationStatus] = syncedBlocks;
814
+ const [worldState, l2Tips, p2p, l1ToL2MessageSource, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
774
815
  // Handle zero as a special case, since the block hash won't match across services if we're changing the prefilled data for the genesis block,
775
816
  // as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
776
817
  // TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
777
- const result = l2BlockSource.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0 || worldState.hash === l2BlockSource.hash && p2p.hash === l2BlockSource.hash && l1ToL2MessageSource.hash === l2BlockSource.hash;
818
+ const result = l2Tips.proposed.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0 || worldState.hash === l2Tips.proposed.hash && p2p.hash === l2Tips.proposed.hash && l1ToL2MessageSource.hash === l2Tips.proposed.hash;
778
819
  if (!result) {
779
820
  this.log.debug(`Sequencer sync check failed`, {
780
821
  worldState,
781
- l2BlockSource,
822
+ l2BlockSource: l2Tips.proposed,
782
823
  p2p,
783
824
  l1ToL2MessageSource
784
825
  });
@@ -790,43 +831,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
790
831
  const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
791
832
  return {
792
833
  checkpointNumber: CheckpointNumber.ZERO,
834
+ checkpointedCheckpointNumber: CheckpointNumber.ZERO,
793
835
  blockNumber: BlockNumber.ZERO,
794
836
  archive,
795
- l1Timestamp,
837
+ hasProposedCheckpoint: false,
838
+ syncedL2Slot,
796
839
  pendingChainValidationStatus
797
840
  };
798
841
  }
799
- const block = await this.l2BlockSource.getL2Block(blockNumber);
800
- if (!block) {
842
+ const blockData = await this.l2BlockSource.getBlockData(blockNumber);
843
+ if (!blockData) {
801
844
  // this shouldn't really happen because a moment ago we checked that all components were in sync
802
- this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
845
+ this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
803
846
  return undefined;
804
847
  }
848
+ const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
805
849
  return {
806
- block,
807
- blockNumber: block.number,
808
- checkpointNumber: block.checkpointNumber,
809
- archive: block.archive.root,
810
- l1Timestamp,
850
+ blockData,
851
+ blockNumber: blockData.header.getBlockNumber(),
852
+ checkpointNumber: blockData.checkpointNumber,
853
+ checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
854
+ archive: blockData.archive.root,
855
+ hasProposedCheckpoint,
856
+ proposedCheckpointData,
857
+ syncedL2Slot,
811
858
  pendingChainValidationStatus
812
859
  };
813
860
  }
814
861
  /**
815
862
  * Checks if we are the proposer for the next slot.
816
863
  * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
817
- */ async checkCanPropose(slot) {
864
+ */ async checkCanPropose(targetSlot) {
818
865
  let proposer;
819
866
  try {
820
- proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
867
+ proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
821
868
  } catch (e) {
822
869
  if (e instanceof NoCommitteeError) {
823
- this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
870
+ if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
871
+ this.lastSlotForNoCommitteeWarning = targetSlot;
872
+ this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
873
+ }
824
874
  return [
825
875
  false,
826
876
  undefined
827
877
  ];
828
878
  }
829
- this.log.error(`Error getting proposer for slot ${slot}`, e);
879
+ this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
830
880
  return [
831
881
  false,
832
882
  undefined
@@ -849,7 +899,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
849
899
  const validatorAddresses = this.validatorClient.getValidatorAddresses();
850
900
  const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
851
901
  if (!weAreProposer) {
852
- this.log.debug(`Cannot propose at slot ${slot} since we are not a proposer`, {
902
+ this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
903
+ targetSlot,
853
904
  validatorAddresses,
854
905
  proposer
855
906
  });
@@ -858,6 +909,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
858
909
  proposer
859
910
  ];
860
911
  }
912
+ this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
913
+ targetSlot,
914
+ proposer
915
+ });
861
916
  return [
862
917
  true,
863
918
  proposer
@@ -867,7 +922,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
867
922
  * Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
868
923
  * This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
869
924
  */ async tryVoteWhenSyncFails(args) {
870
- const { slot } = args;
925
+ const { slot, targetSlot } = args;
871
926
  // Prevent duplicate attempts in the same slot
872
927
  if (this.lastSlotForFallbackVote === slot) {
873
928
  this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
@@ -890,7 +945,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
890
945
  maxAllowedTime
891
946
  });
892
947
  // Check if we're a proposer or proposal is open
893
- const [canPropose, proposer] = await this.checkCanPropose(slot);
948
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
894
949
  if (!canPropose) {
895
950
  this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
896
951
  slot,
@@ -906,8 +961,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
906
961
  attestorAddress,
907
962
  slot
908
963
  });
909
- // Enqueue governance and slashing votes
910
- const voter = new CheckpointVoter(slot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
964
+ // Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
965
+ const voter = new CheckpointVoter(targetSlot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
911
966
  const votesPromises = voter.enqueueVotes();
912
967
  const votes = await Promise.all(votesPromises);
913
968
  if (votes.every((p)=>!p)) {
@@ -954,7 +1009,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
954
1009
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
955
1010
  * and if they fail, any sequencer will try as well.
956
1011
  */ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
957
- const { pendingChainValidationStatus, l1Timestamp } = syncedTo;
1012
+ const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
958
1013
  if (pendingChainValidationStatus.valid) {
959
1014
  return;
960
1015
  }
@@ -965,7 +1020,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
965
1020
  const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
966
1021
  const logData = {
967
1022
  invalidL1Timestamp: invalidCheckpointTimestamp,
968
- l1Timestamp,
1023
+ syncedL2Slot,
969
1024
  invalidCheckpoint: pendingChainValidationStatus.checkpoint,
970
1025
  secondsBeforeInvalidatingBlockAsCommitteeMember,
971
1026
  secondsBeforeInvalidatingBlockAsNonCommitteeMember,
@@ -1056,6 +1111,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
1056
1111
  getValidatorAddresses() {
1057
1112
  return this.validatorClient?.getValidatorAddresses();
1058
1113
  }
1114
+ /** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
1115
+ this.publisherFactory.updateNodeKeyStore(adapter);
1116
+ }
1059
1117
  getConfig() {
1060
1118
  return this.config;
1061
1119
  }
@@ -1,11 +1,9 @@
1
+ import type { Logger } from '@aztec/foundation/log';
1
2
  import type { SequencerMetrics } from './metrics.js';
2
3
  import { SequencerState } from './utils.js';
3
- export declare const MIN_EXECUTION_TIME = 2;
4
- export declare const CHECKPOINT_INITIALIZATION_TIME = 1;
5
- export declare const CHECKPOINT_ASSEMBLE_TIME = 1;
6
4
  export declare class SequencerTimetable {
7
5
  private readonly metrics?;
8
- private readonly log;
6
+ private readonly log?;
9
7
  /**
10
8
  * How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
11
9
  * assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
@@ -59,7 +57,7 @@ export declare class SequencerTimetable {
59
57
  p2pPropagationTime?: number;
60
58
  blockDurationMs?: number;
61
59
  enforce: boolean;
62
- }, metrics?: SequencerMetrics | undefined, log?: import("@aztec/aztec.js/log").Logger);
60
+ }, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
63
61
  getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
64
62
  getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
65
63
  getMaxAllowedTime(state: SequencerState): number | undefined;
@@ -87,4 +85,4 @@ export declare class SequencerTimetable {
87
85
  isLastBlock: boolean;
88
86
  };
89
87
  }
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLGVBQU8sTUFBTSxrQkFBa0IsSUFBSSxDQUFDO0FBQ3BDLGVBQU8sTUFBTSw4QkFBOEIsSUFBSSxDQUFDO0FBQ2hELGVBQU8sTUFBTSx3QkFBd0IsSUFBSSxDQUFDO0FBRTFDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBckV0Qjs7OztPQUlHO0lBQ0gsU0FBZ0Isa0JBQWtCLEVBQUUsTUFBTSxDQUFDO0lBRTNDOzs7O09BSUc7SUFDSCxTQUFnQixvQkFBb0IsRUFBRSxNQUFNLENBQUM7SUFFN0M7Ozs7O09BS0c7SUFDSCxTQUFnQiwwQkFBMEIsRUFBRSxNQUFNLENBQUM7SUFFbkQ7Ozs7T0FJRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUV6Qzs7O09BR0c7SUFDSCxTQUFnQixnQkFBZ0IsRUFBRSxNQUFNLENBQXNCO0lBRTlELHVEQUF1RDtJQUN2RCxTQUFnQiw0QkFBNEIsRUFBRSxNQUFNLENBQWtDO0lBRXRGLG1HQUFtRztJQUNuRyxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0MsbUZBQW1GO0lBQ25GLFNBQWdCLHNCQUFzQixFQUFFLE1BQU0sQ0FBNEI7SUFFMUUsd0NBQXdDO0lBQ3hDLFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3QyxrRkFBa0Y7SUFDbEYsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLDREQUE0RDtJQUM1RCxTQUFnQixPQUFPLEVBQUUsT0FBTyxDQUFDO0lBRWpDLG9HQUFvRztJQUNwRyxTQUFnQixhQUFhLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUVsRCw0RUFBNEU7SUFDNUUsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7S0FDbEIsRUFDZ0IsT0FBTyxDQUFDLDhCQUFrQixFQUMxQixHQUFHLHVDQUFzQyxFQTJFM0Q7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVNwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFyRXZCOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsWUFDRSxJQUFJLEVBQUU7UUFDSixvQkFBb0IsRUFBRSxNQUFNLENBQUM7UUFDN0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO1FBQzFCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztRQUN6QixrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUM1QixlQUFlLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDekIsT0FBTyxFQUFFLE9BQU8sQ0FBQztLQUNsQixFQUNnQixPQUFPLENBQUMsOEJBQWtCLEVBQzFCLEdBQUcsQ0FBQyxvQkFBUSxFQTJFOUI7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,qBAAa,kBAAkB;IAqE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IArEtB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C;;;;;OAKG;IACH,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IAEzC;;;OAGG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,4BAA4B,EAAE,MAAM,CAAkC;IAEtF,mGAAmG;IACnG,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C,mFAAmF;IACnF,SAAgB,sBAAsB,EAAE,MAAM,CAA4B;IAE1E,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,oGAAoG;IACpG,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,4EAA4E;IAC5E,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,YACE,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,uCAAsC,EA2E3D;IAEM,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,SAAS,CAAC;IACN,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,MAAM,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IA2B7D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAiBtE;IAED;;;;;;;;OAQG;IACI,iBAAiB,CACtB,eAAe,EAAE,MAAM,GAErB;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,GAC1D;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,KAAK,CAAA;KAAE,GAC5D;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAwDhE;CACF"}
1
+ {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AASpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,qBAAa,kBAAkB;IAqE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IArEvB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C;;;;;OAKG;IACH,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IAEzC;;;OAGG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,4BAA4B,EAAE,MAAM,CAAkC;IAEtF,mGAAmG;IACnG,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C,mFAAmF;IACnF,SAAgB,sBAAsB,EAAE,MAAM,CAA4B;IAE1E,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,oGAAoG;IACpG,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,4EAA4E;IAC5E,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,YACE,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,CAAC,oBAAQ,EA2E9B;IAEM,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,SAAS,CAAC;IACN,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,MAAM,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IA2B7D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAiBtE;IAED;;;;;;;;OAQG;IACI,iBAAiB,CACtB,eAAe,EAAE,MAAM,GAErB;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,GAC1D;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,KAAK,CAAA;KAAE,GAC5D;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAwDhE;CACF"}
@@ -1,10 +1,6 @@
1
- import { createLogger } from '@aztec/aztec.js/log';
2
- import { DEFAULT_ATTESTATION_PROPAGATION_TIME as DEFAULT_P2P_PROPAGATION_TIME } from '../config.js';
1
+ import { CHECKPOINT_ASSEMBLE_TIME, CHECKPOINT_INITIALIZATION_TIME, DEFAULT_P2P_PROPAGATION_TIME, MIN_EXECUTION_TIME } from '@aztec/stdlib/timetable';
3
2
  import { SequencerTooSlowError } from './errors.js';
4
3
  import { SequencerState } from './utils.js';
5
- export const MIN_EXECUTION_TIME = 2;
6
- export const CHECKPOINT_INITIALIZATION_TIME = 1;
7
- export const CHECKPOINT_ASSEMBLE_TIME = 1;
8
4
  export class SequencerTimetable {
9
5
  metrics;
10
6
  log;
@@ -41,7 +37,7 @@ export class SequencerTimetable {
41
37
  /** Whether assertTimeLeft will throw if not enough time. */ enforce;
42
38
  /** Duration per block when building multiple blocks per slot (undefined = single block per slot) */ blockDuration;
43
39
  /** Maximum number of blocks that can be built in this slot configuration */ maxNumberOfBlocks;
44
- constructor(opts, metrics, log = createLogger('sequencer:timetable')){
40
+ constructor(opts, metrics, log){
45
41
  this.metrics = metrics;
46
42
  this.log = log;
47
43
  this.minExecutionTime = MIN_EXECUTION_TIME;
@@ -84,7 +80,7 @@ export class SequencerTimetable {
84
80
  this.checkpointFinalizationTime;
85
81
  const initializeDeadline = this.aztecSlotDuration - minWorkToDo;
86
82
  this.initializeDeadline = initializeDeadline;
87
- this.log.verbose(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
83
+ this.log?.info(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
88
84
  ethereumSlotDuration: this.ethereumSlotDuration,
89
85
  aztecSlotDuration: this.aztecSlotDuration,
90
86
  l1PublishingTime: this.l1PublishingTime,
@@ -141,7 +137,7 @@ export class SequencerTimetable {
141
137
  throw new SequencerTooSlowError(newState, maxAllowedTime, secondsIntoSlot);
142
138
  }
143
139
  this.metrics?.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), newState);
144
- this.log.trace(`Enough time to transition to ${newState}`, {
140
+ this.log?.trace(`Enough time to transition to ${newState}`, {
145
141
  maxAllowedTime,
146
142
  secondsIntoSlot
147
143
  });
@@ -175,7 +171,7 @@ export class SequencerTimetable {
175
171
  const available = (maxAllowed - secondsIntoSlot) / 2; // Split remaining time: half for execution, half for re-execution
176
172
  const canStart = available >= this.minExecutionTime;
177
173
  const deadline = secondsIntoSlot + available;
178
- this.log.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
174
+ this.log?.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
179
175
  secondsIntoSlot,
180
176
  maxAllowed,
181
177
  available,
@@ -197,7 +193,7 @@ export class SequencerTimetable {
197
193
  if (timeUntilDeadline >= this.minExecutionTime) {
198
194
  // Found an available sub-slot! Is this the last one?
199
195
  const isLastBlock = subSlot === this.maxNumberOfBlocks;
200
- this.log.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
196
+ this.log?.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
201
197
  secondsIntoSlot,
202
198
  deadline,
203
199
  timeUntilDeadline,
@@ -212,7 +208,7 @@ export class SequencerTimetable {
212
208
  }
213
209
  }
214
210
  // No sub-slots available with enough time
215
- this.log.verbose(`No time left to start any more blocks`, {
211
+ this.log?.verbose(`No time left to start any more blocks`, {
216
212
  secondsIntoSlot,
217
213
  maxBlocks: this.maxNumberOfBlocks,
218
214
  initializationOffset: this.initializationOffset
@@ -1,3 +1,3 @@
1
1
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
2
- export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration'>;
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXF1ZW5jZXIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxNQUFNLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUN6QyxpQkFBaUIsRUFDakIsc0JBQXNCLEdBQUcsZUFBZSxHQUFHLGNBQWMsQ0FDMUQsQ0FBQyJ9
2
+ export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration' | 'rollupManaLimit'>;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXF1ZW5jZXIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxNQUFNLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUN6QyxpQkFBaUIsRUFDakIsc0JBQXNCLEdBQUcsZUFBZSxHQUFHLGNBQWMsR0FBRyxpQkFBaUIsQ0FDOUUsQ0FBQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,CAC1D,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,GAAG,iBAAiB,CAC9E,CAAC"}