@aztec/sequencer-client 0.0.1-commit.1142ef1 → 0.0.1-commit.125b3452

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 (94) hide show
  1. package/dest/client/sequencer-client.d.ts +24 -7
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +101 -16
  4. package/dest/config.d.ts +25 -6
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +49 -30
  7. package/dest/global_variable_builder/global_builder.d.ts +2 -4
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +7 -6
  10. package/dest/index.d.ts +2 -2
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -1
  13. package/dest/publisher/config.d.ts +35 -17
  14. package/dest/publisher/config.d.ts.map +1 -1
  15. package/dest/publisher/config.js +106 -42
  16. package/dest/publisher/index.d.ts +2 -1
  17. package/dest/publisher/index.d.ts.map +1 -1
  18. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  19. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  20. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  21. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  22. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  23. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  24. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  25. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  26. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  27. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  28. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  29. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  30. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
  31. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  32. package/dest/publisher/sequencer-publisher-factory.js +27 -2
  33. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  34. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  35. package/dest/publisher/sequencer-publisher-metrics.js +12 -4
  36. package/dest/publisher/sequencer-publisher.d.ts +26 -8
  37. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  38. package/dest/publisher/sequencer-publisher.js +349 -49
  39. package/dest/sequencer/checkpoint_proposal_job.d.ts +33 -10
  40. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  41. package/dest/sequencer/checkpoint_proposal_job.js +217 -66
  42. package/dest/sequencer/checkpoint_voter.d.ts +3 -2
  43. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  44. package/dest/sequencer/checkpoint_voter.js +34 -10
  45. package/dest/sequencer/index.d.ts +1 -2
  46. package/dest/sequencer/index.d.ts.map +1 -1
  47. package/dest/sequencer/index.js +0 -1
  48. package/dest/sequencer/metrics.d.ts +17 -5
  49. package/dest/sequencer/metrics.d.ts.map +1 -1
  50. package/dest/sequencer/metrics.js +111 -30
  51. package/dest/sequencer/sequencer.d.ts +42 -19
  52. package/dest/sequencer/sequencer.d.ts.map +1 -1
  53. package/dest/sequencer/sequencer.js +107 -50
  54. package/dest/sequencer/timetable.d.ts +4 -6
  55. package/dest/sequencer/timetable.d.ts.map +1 -1
  56. package/dest/sequencer/timetable.js +7 -11
  57. package/dest/sequencer/types.d.ts +2 -2
  58. package/dest/sequencer/types.d.ts.map +1 -1
  59. package/dest/test/index.d.ts +3 -5
  60. package/dest/test/index.d.ts.map +1 -1
  61. package/dest/test/mock_checkpoint_builder.d.ts +27 -19
  62. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  63. package/dest/test/mock_checkpoint_builder.js +67 -38
  64. package/dest/test/utils.d.ts +8 -8
  65. package/dest/test/utils.d.ts.map +1 -1
  66. package/dest/test/utils.js +12 -11
  67. package/package.json +29 -28
  68. package/src/client/sequencer-client.ts +135 -18
  69. package/src/config.ts +65 -41
  70. package/src/global_variable_builder/global_builder.ts +6 -5
  71. package/src/index.ts +1 -6
  72. package/src/publisher/config.ts +121 -43
  73. package/src/publisher/index.ts +3 -0
  74. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  75. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  76. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  77. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  78. package/src/publisher/sequencer-publisher-factory.ts +38 -6
  79. package/src/publisher/sequencer-publisher-metrics.ts +7 -3
  80. package/src/publisher/sequencer-publisher.ts +344 -61
  81. package/src/sequencer/checkpoint_proposal_job.ts +306 -81
  82. package/src/sequencer/checkpoint_voter.ts +32 -7
  83. package/src/sequencer/index.ts +0 -1
  84. package/src/sequencer/metrics.ts +124 -32
  85. package/src/sequencer/sequencer.ts +131 -53
  86. package/src/sequencer/timetable.ts +13 -12
  87. package/src/sequencer/types.ts +1 -1
  88. package/src/test/index.ts +2 -4
  89. package/src/test/mock_checkpoint_builder.ts +122 -78
  90. package/src/test/utils.ts +24 -14
  91. package/dest/sequencer/block_builder.d.ts +0 -26
  92. package/dest/sequencer/block_builder.d.ts.map +0 -1
  93. package/dest/sequencer/block_builder.js +0 -129
  94. package/src/sequencer/block_builder.ts +0 -216
@@ -370,7 +370,7 @@ function applyDecs2203RFactory() {
370
370
  function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
371
371
  return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
372
372
  }
373
- var _dec, _dec1, _dec2, _initProto;
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
376
  import { NoCommitteeError } from '@aztec/ethereum/contracts';
@@ -380,7 +380,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
380
380
  import { EthAddress } from '@aztec/foundation/eth-address';
381
381
  import { createLogger } from '@aztec/foundation/log';
382
382
  import { RunningPromise } from '@aztec/foundation/running-promise';
383
- import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
383
+ import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
384
384
  import { SequencerConfigSchema } from '@aztec/stdlib/interfaces/server';
385
385
  import { pickFromSchema } from '@aztec/stdlib/schemas';
386
386
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -396,6 +396,8 @@ import { SequencerState } from './utils.js';
396
396
  export { SequencerState };
397
397
  _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpointProposal'), _dec2 = trackSpan('Seqeuencer.tryVoteWhenSyncFails', ({ slot })=>({
398
398
  [Attributes.SLOT_NUMBER]: slot
399
+ })), _dec3 = trackSpan('Sequencer.tryVoteWhenEscapeHatchOpen', ({ slot })=>({
400
+ [Attributes.SLOT_NUMBER]: slot
399
401
  }));
400
402
  /**
401
403
  * Sequencer client
@@ -436,24 +438,23 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
436
438
  _dec2,
437
439
  2,
438
440
  "tryVoteWhenSyncFails"
441
+ ],
442
+ [
443
+ _dec3,
444
+ 2,
445
+ "tryVoteWhenEscapeHatchOpen"
439
446
  ]
440
447
  ], []));
441
448
  }
442
449
  runningPromise;
443
450
  state;
444
451
  metrics;
445
- /** The last slot for which we attempted to vote when sync failed, to prevent duplicate attempts. */ lastSlotForVoteWhenSyncFailed;
452
+ /** The last slot for which we attempted to perform our voting duties with degraded block production */ lastSlotForFallbackVote;
453
+ /** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
446
454
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
447
455
  /** Last successful checkpoint proposed */ lastCheckpointProposed;
448
456
  /** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
449
457
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
450
- // This shouldn't be here as this gets re-created each time we build/propose a block.
451
- // But we have a number of tests that abuse/rely on this class having a permanent publisher.
452
- // As long as those tests only configure a single publisher they will continue to work.
453
- // This will get re-assigned every time the sequencer goes to build a new block to a publisher that is valid
454
- // for the block proposer.
455
- // TODO(palla/mbps): Remove this field and fix tests
456
- publisher;
457
458
  /** Config for the sequencer */ config;
458
459
  constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
459
460
  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;
@@ -466,7 +467,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
466
467
  }
467
468
  /** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
468
469
  const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
469
- this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowList'));
470
+ this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
470
471
  this.config = merge(this.config, filteredConfig);
471
472
  this.timetable = new SequencerTimetable({
472
473
  ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
@@ -477,9 +478,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
477
478
  enforce: this.config.enforceTimeTable
478
479
  }, this.metrics, this.log);
479
480
  }
480
- /** Initializes the sequencer (precomputes tables and creates a publisher). Takes about 3s. */ async init() {
481
+ /** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
481
482
  getKzg();
482
- this.publisher = (await this.publisherFactory.create(undefined)).publisher;
483
483
  }
484
484
  /** Starts the sequencer and moves to IDLE state. */ start() {
485
485
  this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
@@ -494,7 +494,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
494
494
  this.setState(SequencerState.STOPPING, undefined, {
495
495
  force: true
496
496
  });
497
- this.publisher?.interrupt();
497
+ this.publisherFactory.interruptAll();
498
498
  await this.runningPromise?.stop();
499
499
  this.setState(SequencerState.STOPPED, undefined, {
500
500
  force: true
@@ -509,7 +509,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
509
509
  error: err
510
510
  });
511
511
  if (err instanceof SequencerTooSlowError) {
512
- // TODO(palla/mbps): Add missing states
513
512
  // Log as warn only if we had to abort halfway through the block proposal
514
513
  const logLvl = [
515
514
  SequencerState.INITIALIZING_CHECKPOINT,
@@ -542,7 +541,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
542
541
  this.setState(SequencerState.SYNCHRONIZING, undefined);
543
542
  const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
544
543
  // Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
545
- const checkpointProposalJob = await this.prepareCheckpointProposal(slot, ts, now);
544
+ const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
546
545
  if (!checkpointProposalJob) {
547
546
  return;
548
547
  }
@@ -563,7 +562,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
563
562
  * Prepares the checkpoint proposal by performing all necessary checks and setup.
564
563
  * This is the initial step in the main loop.
565
564
  * @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
566
- */ async prepareCheckpointProposal(slot, ts, now) {
565
+ */ async prepareCheckpointProposal(epoch, slot, ts, now) {
567
566
  // Check we have not already processed this slot (cheapest check)
568
567
  // We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
569
568
  // running against actual time (eg when we use sandbox-style automining)
@@ -588,12 +587,31 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
588
587
  });
589
588
  return undefined;
590
589
  }
590
+ // If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
591
+ // Still perform governance/slashing voting (as proposer) once per slot.
592
+ const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
593
+ if (isEscapeHatchOpen) {
594
+ this.setState(SequencerState.PROPOSER_CHECK, slot);
595
+ const [canPropose, proposer] = await this.checkCanPropose(slot);
596
+ if (canPropose) {
597
+ await this.tryVoteWhenEscapeHatchOpen({
598
+ slot,
599
+ proposer
600
+ });
601
+ } else {
602
+ this.log.trace(`Escape hatch open but we are not proposer, skipping vote-only actions`, {
603
+ slot,
604
+ epoch,
605
+ proposer
606
+ });
607
+ }
608
+ return undefined;
609
+ }
591
610
  // Next checkpoint follows from the last synced one
592
611
  const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
593
612
  const logCtx = {
594
613
  now,
595
- syncedToL1Ts: syncedTo.l1Timestamp,
596
- syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
614
+ syncedToL2Slot: syncedTo.syncedL2Slot,
597
615
  slot,
598
616
  slotTs: ts,
599
617
  checkpointNumber,
@@ -608,12 +626,12 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
608
626
  return undefined;
609
627
  }
610
628
  // Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
611
- if (syncedTo.block && syncedTo.block.header.getSlot() >= slot) {
629
+ if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= slot) {
612
630
  this.log.warn(`Cannot propose block at next L2 slot ${slot} since that slot was taken by block ${syncedTo.blockNumber}`, {
613
631
  ...logCtx,
614
- block: syncedTo.block.header.toInspect()
632
+ block: syncedTo.blockData.header.toInspect()
615
633
  });
616
- this.metrics.recordBlockProposalPrecheckFailed('slot_already_taken');
634
+ this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
617
635
  return undefined;
618
636
  }
619
637
  // We now need to get ourselves a publisher.
@@ -623,7 +641,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
623
641
  const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
624
642
  const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
625
643
  this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
626
- this.publisher = publisher;
627
644
  // In fisherman mode, set the actual proposer's address for simulations
628
645
  if (this.config.fishermanMode && proposer) {
629
646
  publisher.setProposerAddressForSimulation(proposer);
@@ -640,7 +657,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
640
657
  reason: 'Rollup contract check failed',
641
658
  slot
642
659
  });
643
- this.metrics.recordBlockProposalPrecheckFailed('rollup_contract_check_failed');
660
+ this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
644
661
  return undefined;
645
662
  }
646
663
  if (canProposeCheck.slot !== slot) {
@@ -653,7 +670,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
653
670
  reason: 'Slot mismatch',
654
671
  slot
655
672
  });
656
- this.metrics.recordBlockProposalPrecheckFailed('slot_mismatch');
673
+ this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
657
674
  return undefined;
658
675
  }
659
676
  if (canProposeCheck.checkpointNumber !== checkpointNumber) {
@@ -666,19 +683,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
666
683
  reason: 'Block mismatch',
667
684
  slot
668
685
  });
669
- this.metrics.recordBlockProposalPrecheckFailed('block_number_mismatch');
686
+ this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
670
687
  return undefined;
671
688
  }
672
689
  this.lastSlotForCheckpointProposalJob = slot;
690
+ await this.p2pClient.prepareForSlot(slot);
673
691
  this.log.info(`Preparing checkpoint proposal ${checkpointNumber} at slot ${slot}`, {
674
692
  ...logCtx,
675
693
  proposer
676
694
  });
677
695
  // Create and return the checkpoint proposal job
678
- return this.createCheckpointProposalJob(slot, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint);
696
+ return this.createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint);
697
+ }
698
+ createCheckpointProposalJob(epoch, slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint) {
699
+ 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());
679
700
  }
680
- createCheckpointProposalJob(slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint) {
681
- return new CheckpointProposalJob(slot, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, this.validatorClient, this.globalsBuilder, this.p2pClient, this.worldState, this.l1ToL2MessageSource, this.checkpointsBuilder, this.l2BlockSource, this.l1Constants, this.config, this.timetable, this.slasherClient, this.epochCache, this.dateProvider, this.metrics, this, this.setState.bind(this), this.log, this.tracer);
701
+ /**
702
+ * Returns the current sequencer state.
703
+ */ getState() {
704
+ return this.state;
682
705
  }
683
706
  /**
684
707
  * Internal helper for setting the sequencer state and checks if we have enough time left in the slot to transition to the new state.
@@ -720,16 +743,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
720
743
  * Returns whether all dependencies have caught up.
721
744
  * We don't check against the previous block submitted since it may have been reorg'd out.
722
745
  */ async checkSync(args) {
723
- // Check that the archiver and dependencies have synced to the previous L1 slot at least
746
+ // Check that the archiver has fully synced the L2 slot before the one we want to propose in.
724
747
  // TODO(#14766): Archiver reports L1 timestamp based on L1 blocks seen, which means that a missed L1 block will
725
748
  // cause the archiver L1 timestamp to fall behind, and cause this sequencer to start processing one L1 slot later.
726
- const l1Timestamp = await this.l2BlockSource.getL1Timestamp();
727
- const { slot, ts } = args;
728
- if (l1Timestamp === undefined || l1Timestamp + BigInt(this.l1Constants.ethereumSlotDuration) < ts) {
749
+ const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
750
+ const { slot } = args;
751
+ if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
729
752
  this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
730
753
  slot,
731
- ts,
732
- l1Timestamp
754
+ syncedL2Slot
733
755
  });
734
756
  return undefined;
735
757
  }
@@ -765,22 +787,22 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
765
787
  checkpointNumber: CheckpointNumber.ZERO,
766
788
  blockNumber: BlockNumber.ZERO,
767
789
  archive,
768
- l1Timestamp,
790
+ syncedL2Slot,
769
791
  pendingChainValidationStatus
770
792
  };
771
793
  }
772
- const block = await this.l2BlockSource.getL2BlockNew(blockNumber);
773
- if (!block) {
794
+ const blockData = await this.l2BlockSource.getBlockData(blockNumber);
795
+ if (!blockData) {
774
796
  // this shouldn't really happen because a moment ago we checked that all components were in sync
775
- this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
797
+ this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
776
798
  return undefined;
777
799
  }
778
800
  return {
779
- block,
780
- blockNumber: block.number,
781
- checkpointNumber: block.checkpointNumber,
782
- archive: block.archive.root,
783
- l1Timestamp,
801
+ blockData,
802
+ blockNumber: blockData.header.getBlockNumber(),
803
+ checkpointNumber: blockData.checkpointNumber,
804
+ archive: blockData.archive.root,
805
+ syncedL2Slot,
784
806
  pendingChainValidationStatus
785
807
  };
786
808
  }
@@ -793,7 +815,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
793
815
  proposer = await this.epochCache.getProposerAttesterAddressInSlot(slot);
794
816
  } catch (e) {
795
817
  if (e instanceof NoCommitteeError) {
796
- this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
818
+ if (this.lastSlotForNoCommitteeWarning !== slot) {
819
+ this.lastSlotForNoCommitteeWarning = slot;
820
+ this.log.warn(`Cannot propose at next L2 slot ${slot} since the committee does not exist on L1`);
821
+ }
797
822
  return [
798
823
  false,
799
824
  undefined
@@ -842,7 +867,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
842
867
  */ async tryVoteWhenSyncFails(args) {
843
868
  const { slot } = args;
844
869
  // Prevent duplicate attempts in the same slot
845
- if (this.lastSlotForVoteWhenSyncFailed === slot) {
870
+ if (this.lastSlotForFallbackVote === slot) {
846
871
  this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
847
872
  return;
848
873
  }
@@ -872,7 +897,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
872
897
  return;
873
898
  }
874
899
  // Mark this slot as attempted
875
- this.lastSlotForVoteWhenSyncFailed = slot;
900
+ this.lastSlotForFallbackVote = slot;
876
901
  // Get a publisher for voting
877
902
  const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
878
903
  this.log.debug(`Attempting to vote despite sync failure at slot ${slot}`, {
@@ -893,12 +918,41 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
893
918
  await publisher.sendRequests();
894
919
  }
895
920
  /**
896
- * Considers invalidating a checkpoint if the pending chain is invalid. Depends on how long the invalid checkpoint
921
+ * Tries to vote on slashing actions and governance proposals when escape hatch is open.
922
+ * This allows the sequencer to participate in voting without performing checkpoint proposal work.
923
+ */ async tryVoteWhenEscapeHatchOpen(args) {
924
+ const { slot, proposer } = args;
925
+ // Prevent duplicate attempts in the same slot
926
+ if (this.lastSlotForFallbackVote === slot) {
927
+ this.log.trace(`Already attempted to vote in slot ${slot} (escape hatch open, skipping)`);
928
+ return;
929
+ }
930
+ // Mark this slot as attempted
931
+ this.lastSlotForFallbackVote = slot;
932
+ const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
933
+ this.log.debug(`Escape hatch open for slot ${slot}, attempting vote-only actions`, {
934
+ slot,
935
+ attestorAddress
936
+ });
937
+ const voter = new CheckpointVoter(slot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
938
+ const votesPromises = voter.enqueueVotes();
939
+ const votes = await Promise.all(votesPromises);
940
+ if (votes.every((p)=>!p)) {
941
+ this.log.debug(`No votes to enqueue for slot ${slot} (escape hatch open)`);
942
+ return;
943
+ }
944
+ this.log.info(`Voting in slot ${slot} (escape hatch open)`, {
945
+ slot
946
+ });
947
+ await publisher.sendRequests();
948
+ }
949
+ /**
950
+ * Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
897
951
  * has been there without being invalidated and whether the sequencer is in the committee or not. We always
898
952
  * have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
899
953
  * and if they fail, any sequencer will try as well.
900
954
  */ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
901
- const { pendingChainValidationStatus, l1Timestamp } = syncedTo;
955
+ const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
902
956
  if (pendingChainValidationStatus.valid) {
903
957
  return;
904
958
  }
@@ -909,7 +963,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
909
963
  const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
910
964
  const logData = {
911
965
  invalidL1Timestamp: invalidCheckpointTimestamp,
912
- l1Timestamp,
966
+ syncedL2Slot,
913
967
  invalidCheckpoint: pendingChainValidationStatus.checkpoint,
914
968
  secondsBeforeInvalidatingBlockAsCommitteeMember,
915
969
  secondsBeforeInvalidatingBlockAsNonCommitteeMember,
@@ -1000,6 +1054,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
1000
1054
  getValidatorAddresses() {
1001
1055
  return this.validatorClient?.getValidatorAddresses();
1002
1056
  }
1057
+ /** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
1058
+ this.publisherFactory.updateNodeKeyStore(adapter);
1059
+ }
1003
1060
  getConfig() {
1004
1061
  return this.config;
1005
1062
  }
@@ -1,11 +1,9 @@
1
+ import type { Logger } from '@aztec/foundation/log';
1
2
  import type { SequencerMetrics } from './metrics.js';
2
3
  import { SequencerState } from './utils.js';
3
- export declare const MIN_EXECUTION_TIME = 2;
4
- export declare const CHECKPOINT_INITIALIZATION_TIME = 1;
5
- export declare const CHECKPOINT_ASSEMBLE_TIME = 1;
6
4
  export declare class SequencerTimetable {
7
5
  private readonly metrics?;
8
- private readonly log;
6
+ private readonly log?;
9
7
  /**
10
8
  * How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
11
9
  * assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
@@ -59,7 +57,7 @@ export declare class SequencerTimetable {
59
57
  p2pPropagationTime?: number;
60
58
  blockDurationMs?: number;
61
59
  enforce: boolean;
62
- }, metrics?: SequencerMetrics | undefined, log?: import("@aztec/aztec.js/log").Logger);
60
+ }, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
63
61
  getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
64
62
  getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
65
63
  getMaxAllowedTime(state: SequencerState): number | undefined;
@@ -87,4 +85,4 @@ export declare class SequencerTimetable {
87
85
  isLastBlock: boolean;
88
86
  };
89
87
  }
90
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLGVBQU8sTUFBTSxrQkFBa0IsSUFBSSxDQUFDO0FBQ3BDLGVBQU8sTUFBTSw4QkFBOEIsSUFBSSxDQUFDO0FBQ2hELGVBQU8sTUFBTSx3QkFBd0IsSUFBSSxDQUFDO0FBRTFDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBckV0Qjs7OztPQUlHO0lBQ0gsU0FBZ0Isa0JBQWtCLEVBQUUsTUFBTSxDQUFDO0lBRTNDOzs7O09BSUc7SUFDSCxTQUFnQixvQkFBb0IsRUFBRSxNQUFNLENBQUM7SUFFN0M7Ozs7O09BS0c7SUFDSCxTQUFnQiwwQkFBMEIsRUFBRSxNQUFNLENBQUM7SUFFbkQ7Ozs7T0FJRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUV6Qzs7O09BR0c7SUFDSCxTQUFnQixnQkFBZ0IsRUFBRSxNQUFNLENBQXNCO0lBRTlELHVEQUF1RDtJQUN2RCxTQUFnQiw0QkFBNEIsRUFBRSxNQUFNLENBQWtDO0lBRXRGLG1HQUFtRztJQUNuRyxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0MsbUZBQW1GO0lBQ25GLFNBQWdCLHNCQUFzQixFQUFFLE1BQU0sQ0FBNEI7SUFFMUUsd0NBQXdDO0lBQ3hDLFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3QyxrRkFBa0Y7SUFDbEYsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLDREQUE0RDtJQUM1RCxTQUFnQixPQUFPLEVBQUUsT0FBTyxDQUFDO0lBRWpDLG9HQUFvRztJQUNwRyxTQUFnQixhQUFhLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUVsRCw0RUFBNEU7SUFDNUUsU0FBZ0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBRTFDLFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7S0FDbEIsRUFDZ0IsT0FBTyxDQUFDLDhCQUFrQixFQUMxQixHQUFHLHVDQUFzQyxFQTJFM0Q7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVNwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFyRXZCOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsWUFDRSxJQUFJLEVBQUU7UUFDSixvQkFBb0IsRUFBRSxNQUFNLENBQUM7UUFDN0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO1FBQzFCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztRQUN6QixrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUM1QixlQUFlLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDekIsT0FBTyxFQUFFLE9BQU8sQ0FBQztLQUNsQixFQUNnQixPQUFPLENBQUMsOEJBQWtCLEVBQzFCLEdBQUcsQ0FBQyxvQkFBUSxFQTJFOUI7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,qBAAa,kBAAkB;IAqE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IArEtB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C;;;;;OAKG;IACH,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IAEzC;;;OAGG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,4BAA4B,EAAE,MAAM,CAAkC;IAEtF,mGAAmG;IACnG,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C,mFAAmF;IACnF,SAAgB,sBAAsB,EAAE,MAAM,CAA4B;IAE1E,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,oGAAoG;IACpG,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,4EAA4E;IAC5E,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,YACE,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,uCAAsC,EA2E3D;IAEM,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,SAAS,CAAC;IACN,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,MAAM,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IA2B7D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAiBtE;IAED;;;;;;;;OAQG;IACI,iBAAiB,CACtB,eAAe,EAAE,MAAM,GAErB;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,GAC1D;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,KAAK,CAAA;KAAE,GAC5D;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAwDhE;CACF"}
1
+ {"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AASpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,qBAAa,kBAAkB;IAqE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IArEvB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C;;;;;OAKG;IACH,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IAEzC;;;OAGG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,4BAA4B,EAAE,MAAM,CAAkC;IAEtF,mGAAmG;IACnG,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C,mFAAmF;IACnF,SAAgB,sBAAsB,EAAE,MAAM,CAA4B;IAE1E,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,oGAAoG;IACpG,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,4EAA4E;IAC5E,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,YACE,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,CAAC,oBAAQ,EA2E9B;IAEM,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,SAAS,CAAC;IACN,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,MAAM,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IA2B7D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAiBtE;IAED;;;;;;;;OAQG;IACI,iBAAiB,CACtB,eAAe,EAAE,MAAM,GAErB;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,GAC1D;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,KAAK,CAAA;KAAE,GAC5D;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAwDhE;CACF"}
@@ -1,10 +1,6 @@
1
- import { createLogger } from '@aztec/aztec.js/log';
2
- import { DEFAULT_ATTESTATION_PROPAGATION_TIME as DEFAULT_P2P_PROPAGATION_TIME } from '../config.js';
1
+ import { CHECKPOINT_ASSEMBLE_TIME, CHECKPOINT_INITIALIZATION_TIME, DEFAULT_P2P_PROPAGATION_TIME, MIN_EXECUTION_TIME } from '@aztec/stdlib/timetable';
3
2
  import { SequencerTooSlowError } from './errors.js';
4
3
  import { SequencerState } from './utils.js';
5
- export const MIN_EXECUTION_TIME = 2;
6
- export const CHECKPOINT_INITIALIZATION_TIME = 1;
7
- export const CHECKPOINT_ASSEMBLE_TIME = 1;
8
4
  export class SequencerTimetable {
9
5
  metrics;
10
6
  log;
@@ -41,7 +37,7 @@ export class SequencerTimetable {
41
37
  /** Whether assertTimeLeft will throw if not enough time. */ enforce;
42
38
  /** Duration per block when building multiple blocks per slot (undefined = single block per slot) */ blockDuration;
43
39
  /** Maximum number of blocks that can be built in this slot configuration */ maxNumberOfBlocks;
44
- constructor(opts, metrics, log = createLogger('sequencer:timetable')){
40
+ constructor(opts, metrics, log){
45
41
  this.metrics = metrics;
46
42
  this.log = log;
47
43
  this.minExecutionTime = MIN_EXECUTION_TIME;
@@ -84,7 +80,7 @@ export class SequencerTimetable {
84
80
  this.checkpointFinalizationTime;
85
81
  const initializeDeadline = this.aztecSlotDuration - minWorkToDo;
86
82
  this.initializeDeadline = initializeDeadline;
87
- this.log.verbose(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
83
+ this.log?.info(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
88
84
  ethereumSlotDuration: this.ethereumSlotDuration,
89
85
  aztecSlotDuration: this.aztecSlotDuration,
90
86
  l1PublishingTime: this.l1PublishingTime,
@@ -141,7 +137,7 @@ export class SequencerTimetable {
141
137
  throw new SequencerTooSlowError(newState, maxAllowedTime, secondsIntoSlot);
142
138
  }
143
139
  this.metrics?.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), newState);
144
- this.log.trace(`Enough time to transition to ${newState}`, {
140
+ this.log?.trace(`Enough time to transition to ${newState}`, {
145
141
  maxAllowedTime,
146
142
  secondsIntoSlot
147
143
  });
@@ -175,7 +171,7 @@ export class SequencerTimetable {
175
171
  const available = (maxAllowed - secondsIntoSlot) / 2; // Split remaining time: half for execution, half for re-execution
176
172
  const canStart = available >= this.minExecutionTime;
177
173
  const deadline = secondsIntoSlot + available;
178
- this.log.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
174
+ this.log?.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
179
175
  secondsIntoSlot,
180
176
  maxAllowed,
181
177
  available,
@@ -197,7 +193,7 @@ export class SequencerTimetable {
197
193
  if (timeUntilDeadline >= this.minExecutionTime) {
198
194
  // Found an available sub-slot! Is this the last one?
199
195
  const isLastBlock = subSlot === this.maxNumberOfBlocks;
200
- this.log.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
196
+ this.log?.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
201
197
  secondsIntoSlot,
202
198
  deadline,
203
199
  timeUntilDeadline,
@@ -212,7 +208,7 @@ export class SequencerTimetable {
212
208
  }
213
209
  }
214
210
  // No sub-slots available with enough time
215
- this.log.verbose(`No time left to start any more blocks`, {
211
+ this.log?.verbose(`No time left to start any more blocks`, {
216
212
  secondsIntoSlot,
217
213
  maxBlocks: this.maxNumberOfBlocks,
218
214
  initializationOffset: this.initializationOffset
@@ -1,3 +1,3 @@
1
1
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
2
- export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration'>;
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXF1ZW5jZXIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxNQUFNLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUN6QyxpQkFBaUIsRUFDakIsc0JBQXNCLEdBQUcsZUFBZSxHQUFHLGNBQWMsQ0FDMUQsQ0FBQyJ9
2
+ export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration' | 'rollupManaLimit'>;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXF1ZW5jZXIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxNQUFNLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUN6QyxpQkFBaUIsRUFDakIsc0JBQXNCLEdBQUcsZUFBZSxHQUFHLGNBQWMsR0FBRyxpQkFBaUIsQ0FDOUUsQ0FBQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,CAC1D,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,GAAG,iBAAiB,CAC9E,CAAC"}
@@ -1,16 +1,14 @@
1
- import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
1
+ import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
2
2
  import type { PublisherManager } from '@aztec/ethereum/publisher-manager';
3
3
  import type { PublicProcessorFactory } from '@aztec/simulator/server';
4
4
  import type { FullNodeCheckpointsBuilder, ValidatorClient } from '@aztec/validator-client';
5
5
  import { SequencerClient } from '../client/sequencer-client.js';
6
6
  import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
7
- import type { SequencerPublisher } from '../publisher/sequencer-publisher.js';
8
7
  import { Sequencer } from '../sequencer/sequencer.js';
9
8
  import type { SequencerTimetable } from '../sequencer/timetable.js';
10
9
  declare class TestSequencer_ extends Sequencer {
11
10
  publicProcessorFactory: PublicProcessorFactory;
12
11
  timetable: SequencerTimetable;
13
- publisher: SequencerPublisher;
14
12
  publisherFactory: SequencerPublisherFactory;
15
13
  validatorClient: ValidatorClient;
16
14
  checkpointsBuilder: FullNodeCheckpointsBuilder;
@@ -18,8 +16,8 @@ declare class TestSequencer_ extends Sequencer {
18
16
  export type TestSequencer = TestSequencer_;
19
17
  declare class TestSequencerClient_ extends SequencerClient {
20
18
  sequencer: TestSequencer;
21
- publisherManager: PublisherManager<L1TxUtilsWithBlobs>;
19
+ publisherManager: PublisherManager<L1TxUtils>;
22
20
  }
23
21
  export type TestSequencerClient = TestSequencerClient_;
24
22
  export {};
25
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDakYsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMxRSxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3RFLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLGVBQWUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRTNGLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSx5QkFBeUIsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQzdGLE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDOUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3RELE9BQU8sS0FBSyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFcEUsY0FBTSxjQUFlLFNBQVEsU0FBUztJQUNyQixzQkFBc0IsRUFBRSxzQkFBc0IsQ0FBQztJQUMvQyxTQUFTLEVBQUUsa0JBQWtCLENBQUM7SUFDOUIsU0FBUyxFQUFFLGtCQUFrQixDQUFDO0lBQzlCLGdCQUFnQixFQUFFLHlCQUF5QixDQUFDO0lBQzVDLGVBQWUsRUFBRSxlQUFlLENBQUM7SUFDakMsa0JBQWtCLEVBQUUsMEJBQTBCLENBQUM7Q0FDL0Q7QUFFRCxNQUFNLE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQztBQUUzQyxjQUFNLG9CQUFxQixTQUFRLGVBQWU7SUFDakMsU0FBUyxFQUFFLGFBQWEsQ0FBQztJQUN6QixnQkFBZ0IsRUFBRSxnQkFBZ0IsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0NBQ3ZFO0FBRUQsTUFBTSxNQUFNLG1CQUFtQixHQUFHLG9CQUFvQixDQUFDIn0=
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzdELE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN0RSxPQUFPLEtBQUssRUFBRSwwQkFBMEIsRUFBRSxlQUFlLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUzRixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQztBQUM3RixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDdEQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUVwRSxjQUFNLGNBQWUsU0FBUSxTQUFTO0lBQ3JCLHNCQUFzQixFQUFFLHNCQUFzQixDQUFDO0lBQy9DLFNBQVMsRUFBRSxrQkFBa0IsQ0FBQztJQUM5QixnQkFBZ0IsRUFBRSx5QkFBeUIsQ0FBQztJQUM1QyxlQUFlLEVBQUUsZUFBZSxDQUFDO0lBQ2pDLGtCQUFrQixFQUFFLDBCQUEwQixDQUFDO0NBQy9EO0FBRUQsTUFBTSxNQUFNLGFBQWEsR0FBRyxjQUFjLENBQUM7QUFFM0MsY0FBTSxvQkFBcUIsU0FBUSxlQUFlO0lBQ2pDLFNBQVMsRUFBRSxhQUFhLENBQUM7SUFDekIsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUM7Q0FDOUQ7QUFFRCxNQUFNLE1BQU0sbUJBQW1CLEdBQUcsb0JBQW9CLENBQUMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,cAAM,cAAe,SAAQ,SAAS;IACrB,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,gBAAgB,EAAE,yBAAyB,CAAC;IAC5C,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,0BAA0B,CAAC;CAC/D;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,cAAM,oBAAqB,SAAQ,eAAe;IACjC,SAAS,EAAE,aAAa,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;CACvE;AAED,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE3F,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,cAAM,cAAe,SAAQ,SAAS;IACrB,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,gBAAgB,EAAE,yBAAyB,CAAC;IAC5C,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,0BAA0B,CAAC;CAC/D;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,cAAM,oBAAqB,SAAQ,eAAe;IACjC,SAAS,EAAE,aAAa,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;CAC9D;AAED,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC"}