@aztec/sequencer-client 0.0.1-commit.684755437 → 0.0.1-commit.69c59a8b3

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