@aztec/sequencer-client 0.0.1-commit.592b9384 → 0.0.1-commit.59a0419c6

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 (103) hide show
  1. package/dest/client/sequencer-client.d.ts +17 -7
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +63 -30
  4. package/dest/config.d.ts +26 -7
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +52 -36
  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 -16
  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 +47 -17
  20. package/dest/publisher/config.d.ts.map +1 -1
  21. package/dest/publisher/config.js +121 -42
  22. package/dest/publisher/index.d.ts +2 -1
  23. package/dest/publisher/index.d.ts.map +1 -1
  24. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  25. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  26. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  27. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +58 -0
  28. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  29. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  30. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  31. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  32. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  33. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  34. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  35. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  36. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -5
  37. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  38. package/dest/publisher/sequencer-publisher-factory.js +27 -3
  39. package/dest/publisher/sequencer-publisher.d.ts +75 -48
  40. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  41. package/dest/publisher/sequencer-publisher.js +432 -139
  42. package/dest/sequencer/chain_state_overrides.d.ts +25 -0
  43. package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
  44. package/dest/sequencer/chain_state_overrides.js +39 -0
  45. package/dest/sequencer/checkpoint_proposal_job.d.ts +42 -10
  46. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  47. package/dest/sequencer/checkpoint_proposal_job.js +531 -216
  48. package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
  49. package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
  50. package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
  51. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  52. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  53. package/dest/sequencer/checkpoint_voter.js +2 -5
  54. package/dest/sequencer/events.d.ts +7 -1
  55. package/dest/sequencer/events.d.ts.map +1 -1
  56. package/dest/sequencer/metrics.d.ts +21 -8
  57. package/dest/sequencer/metrics.d.ts.map +1 -1
  58. package/dest/sequencer/metrics.js +110 -21
  59. package/dest/sequencer/sequencer.d.ts +45 -17
  60. package/dest/sequencer/sequencer.d.ts.map +1 -1
  61. package/dest/sequencer/sequencer.js +160 -94
  62. package/dest/sequencer/timetable.d.ts +17 -6
  63. package/dest/sequencer/timetable.d.ts.map +1 -1
  64. package/dest/sequencer/timetable.js +51 -46
  65. package/dest/sequencer/types.d.ts +2 -2
  66. package/dest/sequencer/types.d.ts.map +1 -1
  67. package/dest/test/index.d.ts +3 -5
  68. package/dest/test/index.d.ts.map +1 -1
  69. package/dest/test/mock_checkpoint_builder.d.ts +11 -11
  70. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  71. package/dest/test/mock_checkpoint_builder.js +45 -34
  72. package/dest/test/utils.d.ts +3 -3
  73. package/dest/test/utils.d.ts.map +1 -1
  74. package/dest/test/utils.js +5 -4
  75. package/package.json +27 -28
  76. package/src/client/sequencer-client.ts +81 -30
  77. package/src/config.ts +70 -46
  78. package/src/global_variable_builder/README.md +44 -0
  79. package/src/global_variable_builder/fee_predictor.ts +172 -0
  80. package/src/global_variable_builder/fee_provider.ts +75 -0
  81. package/src/global_variable_builder/global_builder.ts +27 -63
  82. package/src/global_variable_builder/index.ts +3 -1
  83. package/src/publisher/config.ts +157 -45
  84. package/src/publisher/index.ts +3 -0
  85. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  86. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +57 -0
  87. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  88. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  89. package/src/publisher/sequencer-publisher-factory.ts +38 -9
  90. package/src/publisher/sequencer-publisher.ts +499 -198
  91. package/src/sequencer/README.md +83 -13
  92. package/src/sequencer/chain_state_overrides.ts +87 -0
  93. package/src/sequencer/checkpoint_proposal_job.ts +678 -237
  94. package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
  95. package/src/sequencer/checkpoint_voter.ts +1 -12
  96. package/src/sequencer/events.ts +6 -1
  97. package/src/sequencer/metrics.ts +124 -26
  98. package/src/sequencer/sequencer.ts +231 -106
  99. package/src/sequencer/timetable.ts +70 -57
  100. package/src/sequencer/types.ts +1 -1
  101. package/src/test/index.ts +2 -4
  102. package/src/test/mock_checkpoint_builder.ts +63 -49
  103. package/src/test/utils.ts +5 -2
@@ -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,18 +450,14 @@ _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;
455
+ /** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
453
456
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
454
457
  /** Last successful checkpoint proposed */ lastCheckpointProposed;
455
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;
456
460
  /** 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
461
  /** Config for the sequencer */ config;
465
462
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
466
463
  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;
@@ -469,24 +466,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
469
466
  this.log = log.createChild('[FISHERMAN]');
470
467
  }
471
468
  this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
469
+ this.checkpointProposalJobMetrics = new CheckpointProposalJobMetrics(telemetry);
472
470
  this.updateConfig(config);
473
471
  }
474
472
  /** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
475
473
  const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
476
- this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowList'));
474
+ this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
477
475
  this.config = merge(this.config, filteredConfig);
476
+ const p2pPropagationTime = this.config.attestationPropagationTime;
478
477
  this.timetable = new SequencerTimetable({
479
478
  ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
480
479
  aztecSlotDuration: this.aztecSlotDuration,
481
480
  l1PublishingTime: this.l1PublishingTime,
482
- p2pPropagationTime: this.config.attestationPropagationTime,
481
+ p2pPropagationTime,
483
482
  blockDurationMs: this.config.blockDurationMs,
484
- enforce: this.config.enforceTimeTable
483
+ enforce: this.config.enforceTimeTable,
484
+ pipelining: this.epochCache.isProposerPipeliningEnabled()
485
485
  }, this.metrics, this.log);
486
486
  }
487
- /** Initializes the sequencer (precomputes tables and creates a publisher). Takes about 3s. */ async init() {
487
+ /** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
488
488
  getKzg();
489
- this.publisher = (await this.publisherFactory.create(undefined)).publisher;
490
489
  }
491
490
  /** Starts the sequencer and moves to IDLE state. */ start() {
492
491
  this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
@@ -496,13 +495,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
496
495
  this.runningPromise.start();
497
496
  this.log.info('Started sequencer');
498
497
  }
498
+ /** Triggers an immediate run of the sequencer, bypassing the polling interval. */ trigger() {
499
+ return this.runningPromise?.trigger();
500
+ }
499
501
  /** Stops the sequencer from building blocks and moves to STOPPED state. */ async stop() {
500
502
  this.log.info(`Stopping sequencer`);
501
503
  this.setState(SequencerState.STOPPING, undefined, {
502
504
  force: true
503
505
  });
504
- this.publisher?.interrupt();
506
+ await this.publisherFactory.stopAll();
505
507
  await this.runningPromise?.stop();
508
+ await this.lastCheckpointProposalJob?.awaitPendingSubmission();
506
509
  this.setState(SequencerState.STOPPED, undefined, {
507
510
  force: true
508
511
  });
@@ -516,7 +519,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
516
519
  error: err
517
520
  });
518
521
  if (err instanceof SequencerTooSlowError) {
519
- // TODO(palla/mbps): Add missing states
520
522
  // Log as warn only if we had to abort halfway through the block proposal
521
523
  const logLvl = [
522
524
  SequencerState.INITIALIZING_CHECKPOINT,
@@ -547,22 +549,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
547
549
  * - Submit checkpoint
548
550
  */ async work() {
549
551
  this.setState(SequencerState.SYNCHRONIZING, undefined);
550
- 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();
551
554
  // 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);
555
+ const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
553
556
  if (!checkpointProposalJob) {
554
557
  return;
555
558
  }
559
+ // Track the job so we can await its pending L1 submission during shutdown
560
+ this.lastCheckpointProposalJob = checkpointProposalJob;
556
561
  // Execute the checkpoint proposal job
557
562
  const checkpoint = await checkpointProposalJob.execute();
558
563
  // Update last checkpoint proposed (currently unused)
559
564
  if (checkpoint) {
560
565
  this.lastCheckpointProposed = checkpoint;
561
566
  }
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;
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;
566
571
  }
567
572
  return checkpoint;
568
573
  }
@@ -570,17 +575,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
570
575
  * Prepares the checkpoint proposal by performing all necessary checks and setup.
571
576
  * This is the initial step in the main loop.
572
577
  * @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)
578
+ */ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
579
+ // Check we have not already processed this target slot (cheapest check)
575
580
  // We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
576
581
  // 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`);
582
+ if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
583
+ this.log.trace(`Target slot ${targetSlot} has already been processed`);
579
584
  return undefined;
580
585
  }
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}`);
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}`);
584
589
  return undefined;
585
590
  }
586
591
  // Check all components are synced to latest as seen by the archiver (queries all subsystems)
@@ -591,16 +596,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
591
596
  if (!syncedTo) {
592
597
  await this.tryVoteWhenSyncFails({
593
598
  slot,
599
+ targetSlot,
594
600
  ts
595
601
  });
596
602
  return undefined;
597
603
  }
598
- // 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.
599
605
  // Still perform governance/slashing voting (as proposer) once per slot.
600
- 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);
601
608
  if (isEscapeHatchOpen) {
602
609
  this.setState(SequencerState.PROPOSER_CHECK, slot);
603
- const [canPropose, proposer] = await this.checkCanPropose(slot);
610
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
604
611
  if (canPropose) {
605
612
  await this.tryVoteWhenEscapeHatchOpen({
606
613
  slot,
@@ -618,29 +625,36 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
618
625
  // Next checkpoint follows from the last synced one
619
626
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
620
627
  const logCtx = {
621
- now,
622
- syncedToL1Ts: syncedTo.l1Timestamp,
623
- syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
628
+ nowSeconds,
629
+ syncedToL2Slot: syncedTo.syncedL2Slot,
624
630
  slot,
631
+ targetSlot,
625
632
  slotTs: ts,
626
633
  checkpointNumber,
627
634
  isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
628
635
  };
629
- // Check that we are a proposer for the next slot
636
+ // Check that we are a proposer for the target slot.
630
637
  this.setState(SequencerState.PROPOSER_CHECK, slot);
631
- const [canPropose, proposer] = await this.checkCanPropose(slot);
638
+ const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
632
639
  // If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
633
640
  if (!canPropose) {
634
641
  await this.considerInvalidatingCheckpoint(syncedTo, slot);
635
642
  return undefined;
636
643
  }
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}`, {
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}`, {
640
654
  ...logCtx,
641
- block: syncedTo.block.header.toInspect()
655
+ block: syncedTo.blockData.header.toInspect()
642
656
  });
643
- this.metrics.recordBlockProposalPrecheckFailed('slot_already_taken');
657
+ this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
644
658
  return undefined;
645
659
  }
646
660
  // We now need to get ourselves a publisher.
@@ -650,37 +664,58 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
650
664
  const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
651
665
  const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
652
666
  this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
653
- this.publisher = publisher;
654
667
  // In fisherman mode, set the actual proposer's address for simulations
655
668
  if (this.config.fishermanMode && proposer) {
656
669
  publisher.setProposerAddressForSimulation(proposer);
657
670
  this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
658
671
  }
659
672
  // 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);
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);
664
699
  if (canProposeCheck === undefined) {
665
700
  this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
666
701
  this.emit('proposer-rollup-check-failed', {
667
702
  reason: 'Rollup contract check failed',
668
703
  slot
669
704
  });
670
- this.metrics.recordBlockProposalPrecheckFailed('rollup_contract_check_failed');
705
+ this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
671
706
  return undefined;
672
707
  }
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}.`, {
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}.`, {
675
710
  ...logCtx,
676
711
  rollup: canProposeCheck,
677
- expectedSlot: slot
712
+ expectedSlot: targetSlot
678
713
  });
679
714
  this.emit('proposer-rollup-check-failed', {
680
715
  reason: 'Slot mismatch',
681
716
  slot
682
717
  });
683
- this.metrics.recordBlockProposalPrecheckFailed('slot_mismatch');
718
+ this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
684
719
  return undefined;
685
720
  }
686
721
  if (canProposeCheck.checkpointNumber !== checkpointNumber) {
@@ -693,19 +728,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
693
728
  reason: 'Block mismatch',
694
729
  slot
695
730
  });
696
- this.metrics.recordBlockProposalPrecheckFailed('block_number_mismatch');
731
+ this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
697
732
  return undefined;
698
733
  }
699
- this.lastSlotForCheckpointProposalJob = slot;
700
- 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}`, {
701
737
  ...logCtx,
702
- proposer
738
+ proposer,
739
+ pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
703
740
  });
704
741
  // Create and return the checkpoint proposal job
705
- 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);
706
743
  }
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());
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);
746
+ }
747
+ /**
748
+ * Returns the current sequencer state.
749
+ */ getState() {
750
+ return this.state;
709
751
  }
710
752
  /**
711
753
  * 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 +789,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
747
789
  * Returns whether all dependencies have caught up.
748
790
  * We don't check against the previous block submitted since it may have been reorg'd out.
749
791
  */ 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) {
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) {
756
798
  this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
757
799
  slot,
758
- ts,
759
- l1Timestamp
800
+ syncedL2Slot
760
801
  });
761
802
  return undefined;
762
803
  }
@@ -765,22 +806,30 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
765
806
  number: syncSummary.latestBlockNumber,
766
807
  hash: syncSummary.latestBlockHash
767
808
  })),
768
- 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
+ })),
769
814
  this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
770
- this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
771
- 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()
772
821
  ]);
773
- const [worldState, l2BlockSource, p2p, l1ToL2MessageSource, pendingChainValidationStatus] = syncedBlocks;
822
+ const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
774
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,
775
824
  // as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
776
825
  // 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;
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;
778
827
  if (!result) {
779
828
  this.log.debug(`Sequencer sync check failed`, {
780
829
  worldState,
781
- l2BlockSource,
830
+ l2BlockSource: l2Tips.proposed,
782
831
  p2p,
783
- l1ToL2MessageSource
832
+ l1ToL2MessageSourceTips
784
833
  });
785
834
  return undefined;
786
835
  }
@@ -790,43 +839,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
790
839
  const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
791
840
  return {
792
841
  checkpointNumber: CheckpointNumber.ZERO,
842
+ checkpointedCheckpointNumber: CheckpointNumber.ZERO,
793
843
  blockNumber: BlockNumber.ZERO,
794
844
  archive,
795
- l1Timestamp,
845
+ hasProposedCheckpoint: false,
846
+ syncedL2Slot,
796
847
  pendingChainValidationStatus
797
848
  };
798
849
  }
799
- const block = await this.l2BlockSource.getL2Block(blockNumber);
800
- if (!block) {
850
+ const blockData = await this.l2BlockSource.getBlockData(blockNumber);
851
+ if (!blockData) {
801
852
  // 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`);
853
+ this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
803
854
  return undefined;
804
855
  }
856
+ const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
805
857
  return {
806
- block,
807
- blockNumber: block.number,
808
- checkpointNumber: block.checkpointNumber,
809
- archive: block.archive.root,
810
- l1Timestamp,
858
+ blockData,
859
+ blockNumber: blockData.header.getBlockNumber(),
860
+ checkpointNumber: blockData.checkpointNumber,
861
+ checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
862
+ archive: blockData.archive.root,
863
+ hasProposedCheckpoint,
864
+ proposedCheckpointData,
865
+ syncedL2Slot,
811
866
  pendingChainValidationStatus
812
867
  };
813
868
  }
814
869
  /**
815
870
  * Checks if we are the proposer for the next slot.
816
871
  * @returns True if we can propose, and the proposer address (undefined if anyone can propose)
817
- */ async checkCanPropose(slot) {
872
+ */ async checkCanPropose(targetSlot) {
818
873
  let proposer;
819
874
  try {
820
- proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
875
+ proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
821
876
  } catch (e) {
822
877
  if (e instanceof NoCommitteeError) {
823
- 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`);
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,
@@ -1056,6 +1119,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
1056
1119
  getValidatorAddresses() {
1057
1120
  return this.validatorClient?.getValidatorAddresses();
1058
1121
  }
1122
+ /** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
1123
+ this.publisherFactory.updateNodeKeyStore(adapter);
1124
+ }
1059
1125
  getConfig() {
1060
1126
  return this.config;
1061
1127
  }
@@ -1,11 +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
- 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?;
7
+ private readonly checkpointTiming;
9
8
  /**
10
9
  * How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
11
10
  * assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
@@ -52,6 +51,13 @@ export declare class SequencerTimetable {
52
51
  readonly blockDuration: number | undefined;
53
52
  /** Maximum number of blocks that can be built in this slot configuration */
54
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;
55
61
  constructor(opts: {
56
62
  ethereumSlotDuration: number;
57
63
  aztecSlotDuration: number;
@@ -59,7 +65,12 @@ export declare class SequencerTimetable {
59
65
  p2pPropagationTime?: number;
60
66
  blockDurationMs?: number;
61
67
  enforce: boolean;
62
- }, 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;
63
74
  getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
64
75
  getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
65
76
  getMaxAllowedTime(state: SequencerState): number | undefined;
@@ -87,4 +98,4 @@ export declare class SequencerTimetable {
87
98
  isLastBlock: boolean;
88
99
  };
89
100
  }
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLGVBQU8sTUFBTSxrQkFBa0IsSUFBSSxDQUFDO0FBQ3BDLGVBQU8sTUFBTSw4QkFBOEIsSUFBSSxDQUFDO0FBQ2hELGVBQU8sTUFBTSx3QkFBd0IsSUFBSSxDQUFDO0FBRTFDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBckV0Qjs7OztPQUlHO0lBQ0gsU0FBZ0Isa0JBQWtCLEVBQUUsTUFBTSxDQUFDO0lBRTNDOzs7O09BSUc7SUFDSCxTQUFnQixvQkFBb0IsRUFBRSxNQUFNLENBQUM7SUFFN0M7Ozs7O09BS0c7SUFDSCxTQUFnQiwwQkFBMEIsRUFBRSxNQUFNLENBQUM7SUFFbkQ7Ozs7T0FJRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUV6Qzs7O09BR0c7SUFDSCxTQUFnQixnQkFBZ0IsRUFBRSxNQUFNLENBQXNCO0lBRTlELHVEQUF1RDtJQUN2RCxTQUFnQiw0QkFBNEIsRUFBRSxNQUFNLENBQWtDO0lBRXRGLG1HQUFtRztJQUNuRyxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0MsbUZBQW1GO0lBQ25GLFNBQWdCLHNCQUFzQixFQUFFLE1BQU0sQ0FBNEI7SUFFMUUsd0NBQXdDO0lBQ3hDLFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3QyxrRkFBa0Y7SUFDbEYsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLDREQUE0RDtJQUM1RCxTQUFnQixPQUFPLEVBQUUsT0FBTyxDQUFDO0lBRWpDLG9HQUFvRztJQUNwRyxTQUFnQixhQUFhLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUVsRCw0RUFBNEU7SUFDNUUsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7S0FDbEIsRUFDZ0IsT0FBTyxDQUFDLDhCQUFrQixFQUMxQixHQUFHLHVDQUFzQyxFQTJFM0Q7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVdwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQWlGM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFqRnZCLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQW1CO0lBRXBEOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMscUZBQXFGO0lBQ3JGLFNBQWdCLFVBQVUsRUFBRSxPQUFPLENBQUM7SUFFcEM7OztPQUdHO0lBQ0gsU0FBZ0IsZ0NBQWdDLEVBQUUsTUFBTSxDQUFDO0lBRXpELFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7UUFDakIsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDO0tBQ3RCLEVBQ2dCLE9BQU8sQ0FBQyw4QkFBa0IsRUFDMUIsR0FBRyxDQUFDLG9CQUFRLEVBd0Q5QjtJQUVNLDZCQUE2QixJQUFJLE1BQU0sQ0FFN0M7SUFFTSxnQ0FBZ0MsSUFBSSxNQUFNLENBRWhEO0lBRU0scUNBQXFDLElBQUksTUFBTSxDQUVyRDtJQUVNLCtCQUErQixJQUFJLE1BQU0sQ0FFL0M7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTRCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
@@ -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;AAWpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,qBAAa,kBAAkB;IAiF3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IAjFvB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IAEpD;;;;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,qFAAqF;IACrF,SAAgB,UAAU,EAAE,OAAO,CAAC;IAEpC;;;OAGG;IACH,SAAgB,gCAAgC,EAAE,MAAM,CAAC;IAEzD,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;QACjB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,CAAC,oBAAQ,EAwD9B;IAEM,6BAA6B,IAAI,MAAM,CAE7C;IAEM,gCAAgC,IAAI,MAAM,CAEhD;IAEM,qCAAqC,IAAI,MAAM,CAErD;IAEM,+BAA+B,IAAI,MAAM,CAE/C;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;IA4B7D,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"}