@aztec/sequencer-client 0.0.1-commit.ec5f612 → 0.0.1-commit.ec7ac5448

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 (63) hide show
  1. package/dest/client/sequencer-client.d.ts +6 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +49 -27
  4. package/dest/config.d.ts +25 -5
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +31 -22
  7. package/dest/global_variable_builder/global_builder.d.ts +15 -8
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +26 -26
  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 +13 -1
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +17 -2
  15. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -5
  16. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.js +16 -3
  18. package/dest/publisher/sequencer-publisher.d.ts +55 -43
  19. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  20. package/dest/publisher/sequencer-publisher.js +180 -116
  21. package/dest/sequencer/chain_state_overrides.d.ts +25 -0
  22. package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
  23. package/dest/sequencer/chain_state_overrides.js +39 -0
  24. package/dest/sequencer/checkpoint_proposal_job.d.ts +24 -9
  25. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  26. package/dest/sequencer/checkpoint_proposal_job.js +331 -211
  27. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  28. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  29. package/dest/sequencer/checkpoint_voter.js +2 -5
  30. package/dest/sequencer/events.d.ts +2 -1
  31. package/dest/sequencer/events.d.ts.map +1 -1
  32. package/dest/sequencer/metrics.d.ts +9 -2
  33. package/dest/sequencer/metrics.d.ts.map +1 -1
  34. package/dest/sequencer/metrics.js +23 -1
  35. package/dest/sequencer/sequencer.d.ts +28 -11
  36. package/dest/sequencer/sequencer.d.ts.map +1 -1
  37. package/dest/sequencer/sequencer.js +134 -74
  38. package/dest/sequencer/timetable.d.ts +17 -3
  39. package/dest/sequencer/timetable.d.ts.map +1 -1
  40. package/dest/sequencer/timetable.js +51 -43
  41. package/dest/sequencer/types.d.ts +2 -2
  42. package/dest/sequencer/types.d.ts.map +1 -1
  43. package/dest/test/mock_checkpoint_builder.d.ts +7 -9
  44. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  45. package/dest/test/mock_checkpoint_builder.js +39 -30
  46. package/package.json +27 -28
  47. package/src/client/sequencer-client.ts +61 -28
  48. package/src/config.ts +39 -24
  49. package/src/global_variable_builder/global_builder.ts +30 -27
  50. package/src/global_variable_builder/index.ts +1 -1
  51. package/src/publisher/config.ts +32 -0
  52. package/src/publisher/sequencer-publisher-factory.ts +18 -6
  53. package/src/publisher/sequencer-publisher.ts +263 -167
  54. package/src/sequencer/README.md +83 -13
  55. package/src/sequencer/chain_state_overrides.ts +87 -0
  56. package/src/sequencer/checkpoint_proposal_job.ts +429 -239
  57. package/src/sequencer/checkpoint_voter.ts +1 -12
  58. package/src/sequencer/events.ts +1 -1
  59. package/src/sequencer/metrics.ts +29 -1
  60. package/src/sequencer/sequencer.ts +194 -81
  61. package/src/sequencer/timetable.ts +64 -52
  62. package/src/sequencer/types.ts +1 -1
  63. package/src/test/mock_checkpoint_builder.ts +51 -48
@@ -373,14 +373,14 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
373
373
  var _dec, _dec1, _dec2, _dec3, _initProto;
374
374
  import { getKzg } from '@aztec/blob-lib';
375
375
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
376
- import { NoCommitteeError } from '@aztec/ethereum/contracts';
376
+ import { NoCommitteeError, SimulationOverridesBuilder } from '@aztec/ethereum/contracts';
377
377
  import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
378
378
  import { merge, omit, pick } from '@aztec/foundation/collection';
379
379
  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';
@@ -454,6 +454,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
454
454
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
455
455
  /** Last successful checkpoint proposed */ lastCheckpointProposed;
456
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;
457
458
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
458
459
  /** Config for the sequencer */ config;
459
460
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
@@ -467,15 +468,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
467
468
  }
468
469
  /** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
469
470
  const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
470
- this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowList'));
471
+ this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
471
472
  this.config = merge(this.config, filteredConfig);
473
+ const p2pPropagationTime = this.config.attestationPropagationTime;
472
474
  this.timetable = new SequencerTimetable({
473
475
  ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
474
476
  aztecSlotDuration: this.aztecSlotDuration,
475
477
  l1PublishingTime: this.l1PublishingTime,
476
- p2pPropagationTime: this.config.attestationPropagationTime,
478
+ p2pPropagationTime,
477
479
  blockDurationMs: this.config.blockDurationMs,
478
- enforce: this.config.enforceTimeTable
480
+ enforce: this.config.enforceTimeTable,
481
+ pipelining: this.epochCache.isProposerPipeliningEnabled()
479
482
  }, this.metrics, this.log);
480
483
  }
481
484
  /** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
@@ -489,13 +492,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
489
492
  this.runningPromise.start();
490
493
  this.log.info('Started sequencer');
491
494
  }
495
+ /** Triggers an immediate run of the sequencer, bypassing the polling interval. */ trigger() {
496
+ return this.runningPromise?.trigger();
497
+ }
492
498
  /** Stops the sequencer from building blocks and moves to STOPPED state. */ async stop() {
493
499
  this.log.info(`Stopping sequencer`);
494
500
  this.setState(SequencerState.STOPPING, undefined, {
495
501
  force: true
496
502
  });
497
- this.publisherFactory.interruptAll();
503
+ await this.publisherFactory.stopAll();
498
504
  await this.runningPromise?.stop();
505
+ await this.lastCheckpointProposalJob?.awaitPendingSubmission();
499
506
  this.setState(SequencerState.STOPPED, undefined, {
500
507
  force: true
501
508
  });
@@ -539,22 +546,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
539
546
  * - Submit checkpoint
540
547
  */ async work() {
541
548
  this.setState(SequencerState.SYNCHRONIZING, undefined);
542
- const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
549
+ const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
550
+ const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
543
551
  // Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
544
- const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
552
+ const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
545
553
  if (!checkpointProposalJob) {
546
554
  return;
547
555
  }
556
+ // Track the job so we can await its pending L1 submission during shutdown
557
+ this.lastCheckpointProposalJob = checkpointProposalJob;
548
558
  // Execute the checkpoint proposal job
549
559
  const checkpoint = await checkpointProposalJob.execute();
550
560
  // Update last checkpoint proposed (currently unused)
551
561
  if (checkpoint) {
552
562
  this.lastCheckpointProposed = checkpoint;
553
563
  }
554
- // Log fee strategy comparison if on fisherman
555
- if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || epoch > this.lastEpochForStrategyComparison)) {
556
- this.logStrategyComparison(epoch, checkpointProposalJob.getPublisher());
557
- this.lastEpochForStrategyComparison = epoch;
564
+ // Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
565
+ if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)) {
566
+ this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
567
+ this.lastEpochForStrategyComparison = targetEpoch;
558
568
  }
559
569
  return checkpoint;
560
570
  }
@@ -562,17 +572,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
562
572
  * Prepares the checkpoint proposal by performing all necessary checks and setup.
563
573
  * This is the initial step in the main loop.
564
574
  * @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
565
- */ async prepareCheckpointProposal(epoch, slot, ts, now) {
566
- // Check we have not already processed this slot (cheapest check)
575
+ */ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
576
+ // Check we have not already processed this target slot (cheapest check)
567
577
  // We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
568
578
  // running against actual time (eg when we use sandbox-style automining)
569
- if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= slot && this.config.enforceTimeTable) {
570
- this.log.trace(`Slot ${slot} has already been processed`);
579
+ if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
580
+ this.log.trace(`Target slot ${targetSlot} has already been processed`);
571
581
  return undefined;
572
582
  }
573
- // But if we have already proposed for this slot, the we definitely have to skip it, automining or not
574
- if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= slot) {
575
- this.log.trace(`Slot ${slot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
583
+ // But if we have already proposed for this slot, then we definitely have to skip it, automining or not
584
+ if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
585
+ this.log.trace(`Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
576
586
  return undefined;
577
587
  }
578
588
  // Check all components are synced to latest as seen by the archiver (queries all subsystems)
@@ -583,16 +593,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
583
593
  if (!syncedTo) {
584
594
  await this.tryVoteWhenSyncFails({
585
595
  slot,
596
+ targetSlot,
586
597
  ts
587
598
  });
588
599
  return undefined;
589
600
  }
590
- // If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
601
+ // If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
591
602
  // Still perform governance/slashing voting (as proposer) once per slot.
592
- const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
603
+ // When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
604
+ const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
593
605
  if (isEscapeHatchOpen) {
594
606
  this.setState(SequencerState.PROPOSER_CHECK, slot);
595
- const [canPropose, proposer] = await this.checkCanPropose(slot);
607
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
596
608
  if (canPropose) {
597
609
  await this.tryVoteWhenEscapeHatchOpen({
598
610
  slot,
@@ -610,25 +622,32 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
610
622
  // Next checkpoint follows from the last synced one
611
623
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
612
624
  const logCtx = {
613
- now,
614
- syncedToL1Ts: syncedTo.l1Timestamp,
615
- syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
625
+ nowSeconds,
626
+ syncedToL2Slot: syncedTo.syncedL2Slot,
616
627
  slot,
628
+ targetSlot,
617
629
  slotTs: ts,
618
630
  checkpointNumber,
619
631
  isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
620
632
  };
621
- // Check that we are a proposer for the next slot
633
+ // Check that we are a proposer for the target slot.
622
634
  this.setState(SequencerState.PROPOSER_CHECK, slot);
623
- const [canPropose, proposer] = await this.checkCanPropose(slot);
635
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
624
636
  // If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
625
637
  if (!canPropose) {
626
638
  await this.considerInvalidatingCheckpoint(syncedTo, slot);
627
639
  return undefined;
628
640
  }
629
- // Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
630
- if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= slot) {
631
- this.log.warn(`Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`, {
641
+ // Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
642
+ // confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
643
+ const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
644
+ if (checkpointNumber > confirmedCkpt + 2) {
645
+ this.log.verbose(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
646
+ return undefined;
647
+ }
648
+ // Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
649
+ if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
650
+ this.log.warn(`Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`, {
632
651
  ...logCtx,
633
652
  block: syncedTo.blockData.header.toInspect()
634
653
  });
@@ -648,10 +667,32 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
648
667
  this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
649
668
  }
650
669
  // Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
651
- const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
652
- // Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
653
- // if all the previous checks are good, but we do it just in case.
654
- const canProposeCheck = await publisher.canProposeAtNextEthBlock(syncedTo.archive, proposer ?? EthAddress.ZERO, invalidateCheckpoint);
670
+ let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
671
+ // Determine the correct archive and L1 state overrides for the canProposeAt check.
672
+ // The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
673
+ // When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
674
+ let archiveForCheck = syncedTo.archive;
675
+ const l1SimulationOverridesBuilder = new SimulationOverridesBuilder();
676
+ if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
677
+ // Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
678
+ // and the archive at that checkpoint so L1 simulation sees the correct chain tip.
679
+ const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
680
+ l1SimulationOverridesBuilder.forPendingCheckpoint(parentCheckpointNumber).withPendingArchive(syncedTo.archive);
681
+ this.metrics.recordPipelineDepth(1);
682
+ this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
683
+ // Clear the invalidation - the proposed checkpoint should handle it.
684
+ invalidateCheckpoint = undefined;
685
+ } else if (invalidateCheckpoint) {
686
+ // After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
687
+ // exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
688
+ archiveForCheck = invalidateCheckpoint.lastArchive;
689
+ l1SimulationOverridesBuilder.forPendingCheckpoint(invalidateCheckpoint.forcePendingCheckpointNumber);
690
+ this.metrics.recordPipelineDepth(0);
691
+ } else {
692
+ this.metrics.recordPipelineDepth(0);
693
+ }
694
+ const simulationOverridesPlan = l1SimulationOverridesBuilder.build();
695
+ const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, simulationOverridesPlan);
655
696
  if (canProposeCheck === undefined) {
656
697
  this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
657
698
  this.emit('proposer-rollup-check-failed', {
@@ -661,11 +702,11 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
661
702
  this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
662
703
  return undefined;
663
704
  }
664
- if (canProposeCheck.slot !== slot) {
665
- 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}.`, {
705
+ if (canProposeCheck.slot !== targetSlot) {
706
+ 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}.`, {
666
707
  ...logCtx,
667
708
  rollup: canProposeCheck,
668
- expectedSlot: slot
709
+ expectedSlot: targetSlot
669
710
  });
670
711
  this.emit('proposer-rollup-check-failed', {
671
712
  reason: 'Slot mismatch',
@@ -687,17 +728,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
687
728
  this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
688
729
  return undefined;
689
730
  }
690
- this.lastSlotForCheckpointProposalJob = slot;
691
- await this.p2pClient.prepareForSlot(slot);
692
- this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, {
731
+ this.lastSlotForCheckpointProposalJob = targetSlot;
732
+ await this.p2pClient.prepareForSlot(targetSlot);
733
+ this.log.info(`Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`, {
693
734
  ...logCtx,
694
- proposer
735
+ proposer,
736
+ pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
695
737
  });
696
738
  // Create and return the checkpoint proposal job
697
- return this.createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint);
739
+ return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
698
740
  }
699
- createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint) {
700
- 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());
741
+ createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
742
+ 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);
701
743
  }
702
744
  /**
703
745
  * Returns the current sequencer state.
@@ -744,16 +786,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
744
786
  * Returns whether all dependencies have caught up.
745
787
  * We don't check against the previous block submitted since it may have been reorg'd out.
746
788
  */ async checkSync(args) {
747
- // Check that the archiver and dependencies have synced to the previous L1 slot at least
748
- // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
749
- // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
750
- const l1Timestamp = await this.l2BlockSource.getL1Timestamp();
751
- const { slot, ts } = args;
752
- if (l1Timestamp === undefined || l1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration) < ts) {
789
+ // Check that the archiver has fully synced the L2 slot before the one we want to propose in.
790
+ // The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
791
+ // See getSyncedL2SlotNumber for how missed L1 blocks are handled.
792
+ const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
793
+ const { slot } = args;
794
+ if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
753
795
  this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
754
796
  slot,
755
- ts,
756
- l1Timestamp
797
+ syncedL2Slot
757
798
  });
758
799
  return undefined;
759
800
  }
@@ -762,22 +803,30 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
762
803
  number: syncSummary.latestBlockNumber,
763
804
  hash: syncSummary.latestBlockHash
764
805
  })),
765
- this.l2BlockSource.getL2Tips().then((t)=>t.proposed),
806
+ this.l2BlockSource.getL2Tips().then((t)=>({
807
+ proposed: t.proposed,
808
+ checkpointed: t.checkpointed,
809
+ proposedCheckpoint: t.proposedCheckpoint
810
+ })),
766
811
  this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
767
- this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
768
- this.l2BlockSource.getPendingChainValidationStatus()
812
+ this.l1ToL2MessageSource.getL2Tips().then((t)=>({
813
+ proposed: t.proposed,
814
+ checkpointed: t.checkpointed
815
+ })),
816
+ this.l2BlockSource.getPendingChainValidationStatus(),
817
+ this.l2BlockSource.getProposedCheckpointOnly()
769
818
  ]);
770
- const [worldState, l2BlockSource, p2p, l1ToL2MessageSource, pendingChainValidationStatus] = syncedBlocks;
819
+ const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
771
820
  // 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,
772
821
  // as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
773
822
  // TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
774
- 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;
823
+ const result = l2Tips.proposed.number === 0 && l2Tips.checkpointed.block.number === 0 && l2Tips.checkpointed.checkpoint.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSourceTips.proposed.number === 0 && l1ToL2MessageSourceTips.checkpointed.block.number === 0 && l1ToL2MessageSourceTips.checkpointed.checkpoint.number === 0 || worldState.hash === l2Tips.proposed.hash && p2p.hash === l2Tips.proposed.hash && l1ToL2MessageSourceTips.proposed.hash === l2Tips.proposed.hash && l1ToL2MessageSourceTips.checkpointed.block.hash === l2Tips.checkpointed.block.hash && l1ToL2MessageSourceTips.checkpointed.checkpoint.hash === l2Tips.checkpointed.checkpoint.hash;
775
824
  if (!result) {
776
825
  this.log.debug(`Sequencer sync check failed`, {
777
826
  worldState,
778
- l2BlockSource,
827
+ l2BlockSource: l2Tips.proposed,
779
828
  p2p,
780
- l1ToL2MessageSource
829
+ l1ToL2MessageSourceTips
781
830
  });
782
831
  return undefined;
783
832
  }
@@ -787,9 +836,11 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
787
836
  const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
788
837
  return {
789
838
  checkpointNumber: CheckpointNumber.ZERO,
839
+ checkpointedCheckpointNumber: CheckpointNumber.ZERO,
790
840
  blockNumber: BlockNumber.ZERO,
791
841
  archive,
792
- l1Timestamp,
842
+ hasProposedCheckpoint: false,
843
+ syncedL2Slot,
793
844
  pendingChainValidationStatus
794
845
  };
795
846
  }
@@ -799,34 +850,38 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
799
850
  this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
800
851
  return undefined;
801
852
  }
853
+ const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
802
854
  return {
803
855
  blockData,
804
856
  blockNumber: blockData.header.getBlockNumber(),
805
857
  checkpointNumber: blockData.checkpointNumber,
858
+ checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
806
859
  archive: blockData.archive.root,
807
- l1Timestamp,
860
+ hasProposedCheckpoint,
861
+ proposedCheckpointData,
862
+ syncedL2Slot,
808
863
  pendingChainValidationStatus
809
864
  };
810
865
  }
811
866
  /**
812
867
  * Checks if we are the proposer for the next slot.
813
868
  * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
814
- */ async checkCanPropose(slot) {
869
+ */ async checkCanPropose(targetSlot) {
815
870
  let proposer;
816
871
  try {
817
- proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
872
+ proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
818
873
  } catch (e) {
819
874
  if (e instanceof NoCommitteeError) {
820
- if (this.lastSlotForNoCommitteeWarning !== slot) {
821
- this.lastSlotForNoCommitteeWarning = slot;
822
- this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
875
+ if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
876
+ this.lastSlotForNoCommitteeWarning = targetSlot;
877
+ this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
823
878
  }
824
879
  return [
825
880
  false,
826
881
  undefined
827
882
  ];
828
883
  }
829
- this.log.error(`Error getting proposer for slot ${slot}`, e);
884
+ this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
830
885
  return [
831
886
  false,
832
887
  undefined
@@ -849,7 +904,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
849
904
  const validatorAddresses = this.validatorClient.getValidatorAddresses();
850
905
  const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
851
906
  if (!weAreProposer) {
852
- this.log.debug(`Cannot propose at slot ${slot} since we are not a proposer`, {
907
+ this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
908
+ targetSlot,
853
909
  validatorAddresses,
854
910
  proposer
855
911
  });
@@ -858,6 +914,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
858
914
  proposer
859
915
  ];
860
916
  }
917
+ this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
918
+ targetSlot,
919
+ proposer
920
+ });
861
921
  return [
862
922
  true,
863
923
  proposer
@@ -867,7 +927,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
867
927
  * 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
928
  * This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
869
929
  */ async tryVoteWhenSyncFails(args) {
870
- const { slot } = args;
930
+ const { slot, targetSlot } = args;
871
931
  // Prevent duplicate attempts in the same slot
872
932
  if (this.lastSlotForFallbackVote === slot) {
873
933
  this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
@@ -890,7 +950,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
890
950
  maxAllowedTime
891
951
  });
892
952
  // Check if we're a proposer or proposal is open
893
- const [canPropose, proposer] = await this.checkCanPropose(slot);
953
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
894
954
  if (!canPropose) {
895
955
  this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
896
956
  slot,
@@ -906,8 +966,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
906
966
  attestorAddress,
907
967
  slot
908
968
  });
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);
969
+ // Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
970
+ const voter = new CheckpointVoter(targetSlot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
911
971
  const votesPromises = voter.enqueueVotes();
912
972
  const votes = await Promise.all(votesPromises);
913
973
  if (votes.every((p)=>!p)) {
@@ -954,7 +1014,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
954
1014
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
955
1015
  * and if they fail, any sequencer will try as well.
956
1016
  */ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
957
- const { pendingChainValidationStatus, l1Timestamp } = syncedTo;
1017
+ const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
958
1018
  if (pendingChainValidationStatus.valid) {
959
1019
  return;
960
1020
  }
@@ -965,7 +1025,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
965
1025
  const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
966
1026
  const logData = {
967
1027
  invalidL1Timestamp: invalidCheckpointTimestamp,
968
- l1Timestamp,
1028
+ syncedL2Slot,
969
1029
  invalidCheckpoint: pendingChainValidationStatus.checkpoint,
970
1030
  secondsBeforeInvalidatingBlockAsCommitteeMember,
971
1031
  secondsBeforeInvalidatingBlockAsNonCommitteeMember,
@@ -1,8 +1,10 @@
1
+ import type { Logger } from '@aztec/foundation/log';
1
2
  import type { SequencerMetrics } from './metrics.js';
2
3
  import { SequencerState } from './utils.js';
3
4
  export declare class SequencerTimetable {
4
5
  private readonly metrics?;
5
- private readonly log;
6
+ private readonly log?;
7
+ private readonly checkpointTiming;
6
8
  /**
7
9
  * How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
8
10
  * assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
@@ -49,6 +51,13 @@ export declare class SequencerTimetable {
49
51
  readonly blockDuration: number | undefined;
50
52
  /** Maximum number of blocks that can be built in this slot configuration */
51
53
  readonly maxNumberOfBlocks: number;
54
+ /** Whether pipelining is enabled (checkpoint finalization deferred to next slot). */
55
+ readonly pipelining: boolean;
56
+ /**
57
+ * How far into the target slot attestation collection can extend when pipelining.
58
+ * Covers validator re-execution (one block duration) plus one-way attestation return.
59
+ */
60
+ readonly pipeliningAttestationGracePeriod: number;
52
61
  constructor(opts: {
53
62
  ethereumSlotDuration: number;
54
63
  aztecSlotDuration: number;
@@ -56,7 +65,12 @@ export declare class SequencerTimetable {
56
65
  p2pPropagationTime?: number;
57
66
  blockDurationMs?: number;
58
67
  enforce: boolean;
59
- }, metrics?: SequencerMetrics | undefined, log?: import("@aztec/aztec.js/log").Logger);
68
+ pipelining?: boolean;
69
+ }, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
70
+ getCheckpointAssemblyDeadline(): number;
71
+ getCheckpointAttestationDeadline(): number;
72
+ getCheckpointAttestationStartDeadline(): number;
73
+ getCheckpointPublishingDeadline(): number;
60
74
  getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
61
75
  getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
62
76
  getMaxAllowedTime(state: SequencerState): number | undefined;
@@ -84,4 +98,4 @@ export declare class SequencerTimetable {
84
98
  isLastBlock: boolean;
85
99
  };
86
100
  }
87
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFTQSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBckV0Qjs7OztPQUlHO0lBQ0gsU0FBZ0Isa0JBQWtCLEVBQUUsTUFBTSxDQUFDO0lBRTNDOzs7O09BSUc7SUFDSCxTQUFnQixvQkFBb0IsRUFBRSxNQUFNLENBQUM7SUFFN0M7Ozs7O09BS0c7SUFDSCxTQUFnQiwwQkFBMEIsRUFBRSxNQUFNLENBQUM7SUFFbkQ7Ozs7T0FJRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUV6Qzs7O09BR0c7SUFDSCxTQUFnQixnQkFBZ0IsRUFBRSxNQUFNLENBQXNCO0lBRTlELHVEQUF1RDtJQUN2RCxTQUFnQiw0QkFBNEIsRUFBRSxNQUFNLENBQWtDO0lBRXRGLG1HQUFtRztJQUNuRyxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0MsbUZBQW1GO0lBQ25GLFNBQWdCLHNCQUFzQixFQUFFLE1BQU0sQ0FBNEI7SUFFMUUsd0NBQXdDO0lBQ3hDLFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3QyxrRkFBa0Y7SUFDbEYsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLDREQUE0RDtJQUM1RCxTQUFnQixPQUFPLEVBQUUsT0FBTyxDQUFDO0lBRWpDLG9HQUFvRztJQUNwRyxTQUFnQixhQUFhLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUVsRCw0RUFBNEU7SUFDNUUsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7S0FDbEIsRUFDZ0IsT0FBTyxDQUFDLDhCQUFrQixFQUMxQixHQUFHLHVDQUFzQyxFQTJFM0Q7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVdwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQWlGM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFqRnZCLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQW1CO0lBRXBEOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMscUZBQXFGO0lBQ3JGLFNBQWdCLFVBQVUsRUFBRSxPQUFPLENBQUM7SUFFcEM7OztPQUdHO0lBQ0gsU0FBZ0IsZ0NBQWdDLEVBQUUsTUFBTSxDQUFDO0lBRXpELFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7UUFDakIsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDO0tBQ3RCLEVBQ2dCLE9BQU8sQ0FBQyw4QkFBa0IsRUFDMUIsR0FBRyxDQUFDLG9CQUFRLEVBd0Q5QjtJQUVNLDZCQUE2QixJQUFJLE1BQU0sQ0FFN0M7SUFFTSxnQ0FBZ0MsSUFBSSxNQUFNLENBRWhEO0lBRU0scUNBQXFDLElBQUksTUFBTSxDQUVyRDtJQUVNLCtCQUErQixJQUFJLE1BQU0sQ0FFL0M7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTRCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AASA,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;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":"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"}