@aztec/sequencer-client 0.0.1-commit.db765a8 → 0.0.1-commit.df81a97b5

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 (54) hide show
  1. package/dest/client/sequencer-client.d.ts +4 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +46 -23
  4. package/dest/config.d.ts +25 -5
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +21 -12
  7. package/dest/global_variable_builder/global_builder.d.ts +13 -7
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +22 -21
  10. package/dest/global_variable_builder/index.d.ts +2 -2
  11. package/dest/global_variable_builder/index.d.ts.map +1 -1
  12. package/dest/publisher/config.d.ts +13 -1
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +17 -2
  15. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
  16. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  17. package/dest/publisher/sequencer-publisher-factory.js +2 -2
  18. package/dest/publisher/sequencer-publisher.d.ts +9 -4
  19. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  20. package/dest/publisher/sequencer-publisher.js +33 -9
  21. package/dest/sequencer/checkpoint_proposal_job.d.ts +13 -7
  22. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  23. package/dest/sequencer/checkpoint_proposal_job.js +166 -115
  24. package/dest/sequencer/events.d.ts +2 -1
  25. package/dest/sequencer/events.d.ts.map +1 -1
  26. package/dest/sequencer/metrics.d.ts +5 -1
  27. package/dest/sequencer/metrics.d.ts.map +1 -1
  28. package/dest/sequencer/metrics.js +11 -0
  29. package/dest/sequencer/sequencer.d.ts +11 -8
  30. package/dest/sequencer/sequencer.d.ts.map +1 -1
  31. package/dest/sequencer/sequencer.js +71 -61
  32. package/dest/sequencer/timetable.d.ts +4 -3
  33. package/dest/sequencer/timetable.d.ts.map +1 -1
  34. package/dest/sequencer/timetable.js +6 -7
  35. package/dest/sequencer/types.d.ts +2 -2
  36. package/dest/sequencer/types.d.ts.map +1 -1
  37. package/dest/test/mock_checkpoint_builder.d.ts +7 -9
  38. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  39. package/dest/test/mock_checkpoint_builder.js +39 -30
  40. package/package.json +27 -28
  41. package/src/client/sequencer-client.ts +56 -21
  42. package/src/config.ts +28 -14
  43. package/src/global_variable_builder/global_builder.ts +22 -23
  44. package/src/global_variable_builder/index.ts +1 -1
  45. package/src/publisher/config.ts +32 -0
  46. package/src/publisher/sequencer-publisher-factory.ts +3 -3
  47. package/src/publisher/sequencer-publisher.ts +39 -11
  48. package/src/sequencer/checkpoint_proposal_job.ts +219 -131
  49. package/src/sequencer/events.ts +1 -1
  50. package/src/sequencer/metrics.ts +14 -0
  51. package/src/sequencer/sequencer.ts +97 -68
  52. package/src/sequencer/timetable.ts +7 -7
  53. package/src/sequencer/types.ts +1 -1
  54. package/src/test/mock_checkpoint_builder.ts +51 -48
@@ -436,8 +436,6 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
436
436
  return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
437
437
  }
438
438
  var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _initProto;
439
- import { NUM_CHECKPOINT_END_MARKER_FIELDS, getNumBlockEndBlobFields } from '@aztec/blob-lib/encoding';
440
- import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB } from '@aztec/constants';
441
439
  import { BlockNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
442
440
  import { randomInt } from '@aztec/foundation/crypto/random';
443
441
  import { flipSignature, generateRecoverableSignature, generateUnrecoverableSignature } from '@aztec/foundation/crypto/secp256k1-signer';
@@ -447,9 +445,10 @@ import { sleep, sleepUntil } from '@aztec/foundation/sleep';
447
445
  import { Timer } from '@aztec/foundation/timer';
448
446
  import { isErrorClass, unfreeze } from '@aztec/foundation/types';
449
447
  import { CommitteeAttestationsAndSigners, MaliciousCommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
450
- import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
448
+ import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
449
+ import { getSlotStartBuildTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
451
450
  import { Gas } from '@aztec/stdlib/gas';
452
- import { NoValidTxsError } from '@aztec/stdlib/interfaces/server';
451
+ import { InsufficientValidTxsError } from '@aztec/stdlib/interfaces/server';
453
452
  import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
454
453
  import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p';
455
454
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
@@ -463,7 +462,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
463
462
  return {
464
463
  // nullish operator needed for tests
465
464
  [Attributes.COINBASE]: this.validatorClient.getCoinbaseForAttestor(this.attestorAddress)?.toString(),
466
- [Attributes.SLOT_NUMBER]: this.slot
465
+ [Attributes.SLOT_NUMBER]: this.targetSlot
467
466
  };
468
467
  }), _dec2 = trackSpan('CheckpointProposalJob.buildBlocksForCheckpoint'), _dec3 = trackSpan('CheckpointProposalJob.waitUntilNextSubslot'), _dec4 = trackSpan('CheckpointProposalJob.buildSingleBlock'), _dec5 = trackSpan('CheckpointProposalJob.waitForMinTxs'), _dec6 = trackSpan('CheckpointProposalJob.waitForAttestations'), _dec7 = trackSpan('CheckpointProposalJob.waitUntilTimeInSlot');
469
468
  /**
@@ -472,8 +471,10 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
472
471
  * as well as enqueueing votes for slashing and governance proposals. This class is created from
473
472
  * the Sequencer once the check for being the proposer for the slot has succeeded.
474
473
  */ export class CheckpointProposalJob {
475
- epoch;
476
- slot;
474
+ slotNow;
475
+ targetSlot;
476
+ epochNow;
477
+ targetEpoch;
477
478
  checkpointNumber;
478
479
  syncedToBlockNumber;
479
480
  proposer;
@@ -543,10 +544,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
543
544
  ], []));
544
545
  }
545
546
  log;
546
- constructor(epoch, slot, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
547
+ constructor(slotNow, targetSlot, epochNow, targetEpoch, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
547
548
  proposer, publisher, attestorAddress, invalidateCheckpoint, validatorClient, globalsBuilder, p2pClient, worldState, l1ToL2MessageSource, l2BlockSource, checkpointsBuilder, blockSink, l1Constants, config, timetable, slasherClient, epochCache, dateProvider, metrics, eventEmitter, setStateFn, tracer, bindings){
548
- this.epoch = epoch;
549
- this.slot = slot;
549
+ this.slotNow = slotNow;
550
+ this.targetSlot = targetSlot;
551
+ this.epochNow = epochNow;
552
+ this.targetEpoch = targetEpoch;
550
553
  this.checkpointNumber = checkpointNumber;
551
554
  this.syncedToBlockNumber = syncedToBlockNumber;
552
555
  this.proposer = proposer;
@@ -574,9 +577,15 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
574
577
  _initProto(this);
575
578
  this.log = createLogger('sequencer:checkpoint-proposal', {
576
579
  ...bindings,
577
- instanceId: `slot-${slot}`
580
+ instanceId: `slot-${this.slotNow}`
578
581
  });
579
582
  }
583
+ /** The wall-clock slot during which the proposer builds. */ get slot() {
584
+ return this.slotNow;
585
+ }
586
+ /** The wall-clock epoch. */ get epoch() {
587
+ return this.epochNow;
588
+ }
580
589
  /**
581
590
  * Executes the checkpoint proposal job.
582
591
  * Returns the published checkpoint if successful, undefined otherwise.
@@ -584,7 +593,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
584
593
  // Enqueue governance and slashing votes (returns promises that will be awaited later)
585
594
  // In fisherman mode, we simulate slashing but don't actually publish to L1
586
595
  // These are constant for the whole slot, so we only enqueue them once
587
- const votesPromises = new CheckpointVoter(this.slot, this.publisher, this.attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log).enqueueVotes();
596
+ const votesPromises = new CheckpointVoter(this.targetSlot, this.publisher, this.attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log).enqueueVotes();
588
597
  // Build and propose the checkpoint. This will enqueue the request on the publisher if a checkpoint is built.
589
598
  const checkpoint = await this.proposeCheckpoint();
590
599
  // Wait until the voting promises have resolved, so all requests are enqueued (not sent)
@@ -597,6 +606,24 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
597
606
  await this.handleCheckpointEndAsFisherman(checkpoint);
598
607
  return;
599
608
  }
609
+ // If pipelining, wait until the submission slot so L1 recognizes the pipelined proposer
610
+ if (this.epochCache.isProposerPipeliningEnabled()) {
611
+ const submissionSlotTimestamp = getTimestampForSlot(this.targetSlot, this.l1Constants) - BigInt(this.l1Constants.ethereumSlotDuration);
612
+ this.log.info(`Waiting until submission slot ${this.targetSlot} for L1 submission`, {
613
+ slot: this.slot,
614
+ submissionSlot: this.targetSlot,
615
+ submissionSlotTimestamp
616
+ });
617
+ await sleepUntil(new Date(Number(submissionSlotTimestamp) * 1000), this.dateProvider.nowAsDate());
618
+ // After waking, verify the parent checkpoint wasn't pruned during the sleep.
619
+ // We check L1's pending tip directly instead of canProposeAt, which also validates the proposer
620
+ // identity and would fail because the timestamp resolves to a different slot's proposer.
621
+ const l1Tips = await this.publisher.rollupContract.getTips();
622
+ if (l1Tips.pending < this.checkpointNumber - 1) {
623
+ this.log.warn(`Parent checkpoint was pruned during pipelining sleep (L1 pending=${l1Tips.pending}, expected>=${this.checkpointNumber - 1}), skipping L1 submission for checkpoint ${this.checkpointNumber}`);
624
+ return undefined;
625
+ }
626
+ }
600
627
  // Then send everything to L1
601
628
  const l1Response = await this.publisher.sendRequests();
602
629
  const proposedAction = l1Response?.successfulActions.find((a)=>a === 'propose');
@@ -628,19 +655,26 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
628
655
  const coinbase = this.validatorClient.getCoinbaseForAttestor(this.attestorAddress);
629
656
  const feeRecipient = this.validatorClient.getFeeRecipientForAttestor(this.attestorAddress);
630
657
  // Start the checkpoint
631
- this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.slot);
632
- this.metrics.incOpenSlot(this.slot, this.proposer?.toString() ?? 'unknown');
658
+ this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.targetSlot);
659
+ this.log.info(`Starting checkpoint proposal`, {
660
+ buildSlot: this.slot,
661
+ submissionSlot: this.targetSlot,
662
+ pipelining: this.epochCache.isProposerPipeliningEnabled(),
663
+ proposer: this.proposer?.toString(),
664
+ coinbase: coinbase.toString()
665
+ });
666
+ this.metrics.incOpenSlot(this.targetSlot, this.proposer?.toString() ?? 'unknown');
633
667
  // Enqueues checkpoint invalidation (constant for the whole slot)
634
668
  if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
635
669
  this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
636
670
  }
637
671
  // Create checkpoint builder for the slot
638
- const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.slot);
672
+ const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.targetSlot);
639
673
  // Collect L1 to L2 messages for the checkpoint and compute their hash
640
674
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
641
675
  const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
642
676
  // Collect the out hashes of all the checkpoints before this one in the same epoch
643
- const previousCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(this.epoch)).filter((c)=>c.checkpointNumber < this.checkpointNumber).map((c)=>c.checkpointOutHash);
677
+ const previousCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(this.targetEpoch)).filter((c)=>c.checkpointNumber < this.checkpointNumber).map((c)=>c.checkpointOutHash);
644
678
  // Get the fee asset price modifier from the oracle
645
679
  const feeAssetPriceModifier = await this.publisher.getFeeAssetPriceModifier();
646
680
  const fork = _ts_add_disposable_resource(env, await this.worldState.fork(this.syncedToBlockNumber, {
@@ -675,18 +709,18 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
675
709
  throw err;
676
710
  }
677
711
  if (blocksInCheckpoint.length === 0) {
678
- this.log.warn(`No blocks were built for slot ${this.slot}`, {
679
- slot: this.slot
712
+ this.log.warn(`No blocks were built for slot ${this.targetSlot}`, {
713
+ slot: this.targetSlot
680
714
  });
681
715
  this.eventEmitter.emit('checkpoint-empty', {
682
- slot: this.slot
716
+ slot: this.targetSlot
683
717
  });
684
718
  return undefined;
685
719
  }
686
720
  const minBlocksForCheckpoint = this.config.minBlocksForCheckpoint;
687
721
  if (minBlocksForCheckpoint !== undefined && blocksInCheckpoint.length < minBlocksForCheckpoint) {
688
722
  this.log.warn(`Checkpoint has fewer blocks than minimum (${blocksInCheckpoint.length} < ${minBlocksForCheckpoint}), skipping proposal`, {
689
- slot: this.slot,
723
+ slot: this.targetSlot,
690
724
  blocksBuilt: blocksInCheckpoint.length,
691
725
  minBlocksForCheckpoint
692
726
  });
@@ -694,14 +728,30 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
694
728
  }
695
729
  // Assemble and broadcast the checkpoint proposal, including the last block that was not
696
730
  // broadcasted yet, and wait to collect the committee attestations.
697
- this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.slot);
731
+ this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.targetSlot);
698
732
  const checkpoint = await checkpointBuilder.completeCheckpoint();
733
+ // Final validation: per-block limits are only checked if the operator set them explicitly.
734
+ // Otherwise, checkpoint-level budgets were already enforced by the redistribution logic.
735
+ try {
736
+ validateCheckpoint(checkpoint, {
737
+ rollupManaLimit: this.l1Constants.rollupManaLimit,
738
+ maxL2BlockGas: this.config.maxL2BlockGas,
739
+ maxDABlockGas: this.config.maxDABlockGas,
740
+ maxTxsPerBlock: this.config.maxTxsPerBlock,
741
+ maxTxsPerCheckpoint: this.config.maxTxsPerCheckpoint
742
+ });
743
+ } catch (err) {
744
+ this.log.error(`Built an invalid checkpoint at slot ${this.slot} (skipping proposal)`, err, {
745
+ checkpoint: checkpoint.header.toInspect()
746
+ });
747
+ return undefined;
748
+ }
699
749
  // Record checkpoint-level build metrics
700
750
  this.metrics.recordCheckpointBuild(checkpointBuildTimer.ms(), blocksInCheckpoint.length, checkpoint.getStats().txCount, Number(checkpoint.header.totalManaUsed.toBigInt()));
701
751
  // Do not collect attestations nor publish to L1 in fisherman mode
702
752
  if (this.config.fishermanMode) {
703
- this.log.info(`Built checkpoint for slot ${this.slot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
704
- slot: this.slot,
753
+ this.log.info(`Built checkpoint for slot ${this.targetSlot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
754
+ slot: this.targetSlot,
705
755
  checkpoint: checkpoint.header.toInspect(),
706
756
  blocksBuilt: blocksInCheckpoint.length
707
757
  });
@@ -718,7 +768,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
718
768
  const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier, lastBlock, this.proposer, checkpointProposalOptions);
719
769
  const blockProposedAt = this.dateProvider.now();
720
770
  await this.p2pClient.broadcastCheckpointProposal(proposal);
721
- this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.slot);
771
+ this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.targetSlot);
722
772
  const attestations = await this.waitForAttestations(proposal);
723
773
  const blockAttestedAt = this.dateProvider.now();
724
774
  this.metrics.recordCheckpointAttestationDelay(blockAttestedAt - blockProposedAt);
@@ -726,7 +776,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
726
776
  const signer = this.proposer ?? this.publisher.getSenderAddress();
727
777
  let attestationsSignature;
728
778
  try {
729
- attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.slot, this.checkpointNumber);
779
+ attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.targetSlot, this.checkpointNumber);
730
780
  } catch (err) {
731
781
  // We shouldn't really get here since we yield to another HA node
732
782
  // as soon as we see these errors when creating block or checkpoint proposals.
@@ -736,10 +786,10 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
736
786
  throw err;
737
787
  }
738
788
  // Enqueue publishing the checkpoint to L1
739
- this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.slot);
789
+ this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.targetSlot);
740
790
  const aztecSlotDuration = this.l1Constants.slotDuration;
741
- const slotStartBuildTimestamp = this.getSlotStartBuildTimestamp();
742
- const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
791
+ const submissionSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
792
+ const txTimeoutAt = new Date((submissionSlotStart + aztecSlotDuration) * 1000);
743
793
  // If we have been configured to potentially skip publishing checkpoint then roll the dice here
744
794
  if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
745
795
  const result = Math.max(0, randomInt(100));
@@ -775,8 +825,6 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
775
825
  const blocksInCheckpoint = [];
776
826
  const txHashesAlreadyIncluded = new Set();
777
827
  const initialBlockNumber = BlockNumber(this.syncedToBlockNumber + 1);
778
- // Remaining blob fields available for blocks (checkpoint end marker already subtracted)
779
- let remainingBlobFields = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS;
780
828
  // Last block in the checkpoint will usually be flagged as pending broadcast, so we send it along with the checkpoint proposal
781
829
  let blockPendingBroadcast = undefined;
782
830
  while(true){
@@ -787,7 +835,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
787
835
  const timingInfo = this.timetable.canStartNextBlock(secondsIntoSlot);
788
836
  if (!timingInfo.canStart) {
789
837
  this.log.debug(`Not enough time left in slot to start another block`, {
790
- slot: this.slot,
838
+ slot: this.targetSlot,
791
839
  blocksBuilt,
792
840
  secondsIntoSlot
793
841
  });
@@ -802,8 +850,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
802
850
  buildDeadline: timingInfo.deadline ? new Date((this.getSlotStartBuildTimestamp() + timingInfo.deadline) * 1000) : undefined,
803
851
  blockNumber,
804
852
  indexWithinCheckpoint,
805
- txHashesAlreadyIncluded,
806
- remainingBlobFields
853
+ txHashesAlreadyIncluded
807
854
  });
808
855
  // TODO(palla/mbps): Review these conditions. We may want to keep trying in some scenarios.
809
856
  if (!buildResult && timingInfo.isLastBlock) {
@@ -817,33 +864,23 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
817
864
  } else if ('error' in buildResult) {
818
865
  // If there was an error building the block, just exit the loop and give up the rest of the slot
819
866
  if (!(buildResult.error instanceof SequencerInterruptedError)) {
820
- this.log.warn(`Halting block building for slot ${this.slot}`, {
821
- slot: this.slot,
867
+ this.log.warn(`Halting block building for slot ${this.targetSlot}`, {
868
+ slot: this.targetSlot,
822
869
  blocksBuilt,
823
870
  error: buildResult.error
824
871
  });
825
872
  }
826
873
  break;
827
874
  }
828
- const { block, usedTxs, remainingBlobFields: newRemainingBlobFields } = buildResult;
875
+ const { block, usedTxs } = buildResult;
829
876
  blocksInCheckpoint.push(block);
830
- // Update remaining blob fields for the next block
831
- remainingBlobFields = newRemainingBlobFields;
832
- // Sync the proposed block to the archiver to make it available
833
- // Note that the checkpoint builder uses its own fork so it should not need to wait for this syncing
834
- // Eventually we should refactor the checkpoint builder to not need a separate long-lived fork
835
- // Fire and forget - don't block the critical path, but log errors
836
- this.syncProposedBlockToArchiver(block).catch((err)=>{
837
- this.log.error(`Failed to sync proposed block ${block.number} to archiver`, {
838
- blockNumber: block.number,
839
- err
840
- });
841
- });
842
877
  usedTxs.forEach((tx)=>txHashesAlreadyIncluded.add(tx.txHash.toString()));
843
- // If this is the last block, exit the loop now so we start collecting attestations
878
+ // If this is the last block, sync it to the archiver and exit the loop
879
+ // so we can build the checkpoint and start collecting attestations.
844
880
  if (timingInfo.isLastBlock) {
845
- this.log.verbose(`Completed final block ${blockNumber} for slot ${this.slot}`, {
846
- slot: this.slot,
881
+ await this.syncProposedBlockToArchiver(block);
882
+ this.log.verbose(`Completed final block ${blockNumber} for slot ${this.targetSlot}`, {
883
+ slot: this.targetSlot,
847
884
  blockNumber,
848
885
  blocksBuilt
849
886
  });
@@ -853,17 +890,22 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
853
890
  };
854
891
  break;
855
892
  }
856
- // For non-last blocks, broadcast the block proposal (unless we're in fisherman mode)
857
- // If the block is the last one, we'll broadcast it along with the checkpoint at the end of the loop
858
- if (!this.config.fishermanMode) {
859
- const proposal = await this.validatorClient.createBlockProposal(block.header, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
860
- await this.p2pClient.broadcastProposal(proposal);
861
- }
893
+ // Broadcast the block proposal (unless we're in fisherman mode) unless the block is the last one,
894
+ // in which case we'll broadcast it along with the checkpoint at the end of the loop.
895
+ // Note that we only send the block to the archiver if we manage to create the proposal, so if there's
896
+ // a HA error we don't pollute our archiver with a block that won't make it to the chain.
897
+ const proposal = await this.createBlockProposal(block, inHash, usedTxs, blockProposalOptions);
898
+ // Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal.
899
+ // We wait for the sync to succeed, as this helps catch consistency errors, even if it means we lose some time for block-building.
900
+ // If this throws, we abort the entire checkpoint.
901
+ await this.syncProposedBlockToArchiver(block);
902
+ // Once we have a signed proposal and the archiver agreed with our proposed block, then we broadcast it.
903
+ proposal && await this.p2pClient.broadcastProposal(proposal);
862
904
  // Wait until the next block's start time
863
905
  await this.waitUntilNextSubslot(timingInfo.deadline);
864
906
  }
865
- this.log.verbose(`Block building loop completed for slot ${this.slot}`, {
866
- slot: this.slot,
907
+ this.log.verbose(`Block building loop completed for slot ${this.targetSlot}`, {
908
+ slot: this.targetSlot,
867
909
  blocksBuilt: blocksInCheckpoint.length
868
910
  });
869
911
  return {
@@ -871,33 +913,39 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
871
913
  blockPendingBroadcast
872
914
  };
873
915
  }
916
+ /** Creates a block proposal for a given block via the validator client (unless in fisherman mode) */ createBlockProposal(block, inHash, usedTxs, blockProposalOptions) {
917
+ if (this.config.fishermanMode) {
918
+ this.log.info(`Skipping block proposal for block ${block.number} in fisherman mode`);
919
+ return Promise.resolve(undefined);
920
+ }
921
+ return this.validatorClient.createBlockProposal(block.header, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
922
+ }
874
923
  /** Sleeps until it is time to produce the next block in the slot */ async waitUntilNextSubslot(nextSubslotStart) {
875
- this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.slot);
924
+ this.setStateFn(SequencerState.WAITING_UNTIL_NEXT_BLOCK, this.targetSlot);
876
925
  this.log.verbose(`Waiting until time for the next block at ${nextSubslotStart}s into slot`, {
877
- slot: this.slot
926
+ slot: this.targetSlot
878
927
  });
879
928
  await this.waitUntilTimeInSlot(nextSubslotStart);
880
929
  }
881
930
  /** Builds a single block. Called from the main block building loop. */ async buildSingleBlock(checkpointBuilder, opts) {
882
- const { blockTimestamp, forceCreate, blockNumber, indexWithinCheckpoint, buildDeadline, txHashesAlreadyIncluded, remainingBlobFields } = opts;
883
- this.log.verbose(`Preparing block ${blockNumber} index ${indexWithinCheckpoint} at checkpoint ${this.checkpointNumber} for slot ${this.slot}`, {
931
+ const { blockTimestamp, forceCreate, blockNumber, indexWithinCheckpoint, buildDeadline, txHashesAlreadyIncluded } = opts;
932
+ this.log.verbose(`Preparing block ${blockNumber} index ${indexWithinCheckpoint} at checkpoint ${this.checkpointNumber} for slot ${this.targetSlot}`, {
884
933
  ...checkpointBuilder.getConstantData(),
885
934
  ...opts
886
935
  });
887
936
  try {
888
937
  // Wait until we have enough txs to build the block
889
- const minTxs = this.config.minTxsPerBlock;
890
- const { availableTxs, canStartBuilding } = await this.waitForMinTxs(opts);
938
+ const { availableTxs, canStartBuilding, minTxs } = await this.waitForMinTxs(opts);
891
939
  if (!canStartBuilding) {
892
- this.log.warn(`Not enough txs to build block ${blockNumber} at index ${indexWithinCheckpoint} in slot ${this.slot} (got ${availableTxs} txs but needs ${minTxs})`, {
940
+ this.log.warn(`Not enough txs to build block ${blockNumber} at index ${indexWithinCheckpoint} in slot ${this.targetSlot} (got ${availableTxs} txs but needs ${minTxs})`, {
893
941
  blockNumber,
894
- slot: this.slot,
942
+ slot: this.targetSlot,
895
943
  indexWithinCheckpoint
896
944
  });
897
945
  this.eventEmitter.emit('block-tx-count-check-failed', {
898
946
  minTxs,
899
947
  availableTxs,
900
- slot: this.slot
948
+ slot: this.targetSlot
901
949
  });
902
950
  this.metrics.recordBlockProposalFailed('insufficient_txs');
903
951
  return undefined;
@@ -905,48 +953,48 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
905
953
  // Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
906
954
  // just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
907
955
  const pendingTxs = filter(this.p2pClient.iterateEligiblePendingTxs(), (tx)=>!txHashesAlreadyIncluded.has(tx.txHash.toString()));
908
- this.log.debug(`Building block ${blockNumber} at index ${indexWithinCheckpoint} for slot ${this.slot} with ${availableTxs} available txs`, {
909
- slot: this.slot,
956
+ this.log.debug(`Building block ${blockNumber} at index ${indexWithinCheckpoint} for slot ${this.targetSlot} with ${availableTxs} available txs`, {
957
+ slot: this.targetSlot,
910
958
  blockNumber,
911
959
  indexWithinCheckpoint
912
960
  });
913
- this.setStateFn(SequencerState.CREATING_BLOCK, this.slot);
914
- // Calculate blob fields limit for txs (remaining capacity - this block's end overhead)
915
- const blockEndOverhead = getNumBlockEndBlobFields(indexWithinCheckpoint === 0);
916
- const maxBlobFieldsForTxs = remainingBlobFields - blockEndOverhead;
961
+ this.setStateFn(SequencerState.CREATING_BLOCK, this.targetSlot);
962
+ // Per-block limits are operator overrides (from SEQ_MAX_L2_BLOCK_GAS etc.) further capped
963
+ // by remaining checkpoint-level budgets inside CheckpointBuilder before each block is built.
964
+ // minValidTxs is passed into the builder so it can reject the block *before* updating state.
965
+ const minValidTxs = forceCreate ? 0 : this.config.minValidTxsPerBlock ?? minTxs;
917
966
  const blockBuilderOptions = {
918
967
  maxTransactions: this.config.maxTxsPerBlock,
919
- maxBlockSize: this.config.maxBlockSizeInBytes,
920
- maxBlockGas: new Gas(this.config.maxDABlockGas, this.config.maxL2BlockGas),
921
- maxBlobFields: maxBlobFieldsForTxs,
922
- deadline: buildDeadline
968
+ maxBlockGas: this.config.maxL2BlockGas !== undefined || this.config.maxDABlockGas !== undefined ? new Gas(this.config.maxDABlockGas ?? Infinity, this.config.maxL2BlockGas ?? Infinity) : undefined,
969
+ deadline: buildDeadline,
970
+ isBuildingProposal: true,
971
+ minValidTxs,
972
+ maxBlocksPerCheckpoint: this.timetable.maxNumberOfBlocks,
973
+ perBlockAllocationMultiplier: this.config.perBlockAllocationMultiplier
923
974
  };
924
- // Actually build the block by executing txs
975
+ // Actually build the block by executing txs. The builder throws InsufficientValidTxsError
976
+ // if the number of successfully processed txs is below minValidTxs, ensuring state is not
977
+ // updated for blocks that will be discarded.
925
978
  const buildResult = await this.buildSingleBlockWithCheckpointBuilder(checkpointBuilder, pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
926
979
  // If any txs failed during execution, drop them from the mempool so we don't pick them up again
927
980
  await this.dropFailedTxsFromP2P(buildResult.failedTxs);
928
- // Check if we have created a block with enough txs. If there were invalid txs in the pool, or if execution took
929
- // too long, then we may not get to minTxsPerBlock after executing public functions.
930
- const minValidTxs = this.config.minValidTxsPerBlock ?? minTxs;
931
- const numTxs = buildResult.status === 'no-valid-txs' ? 0 : buildResult.numTxs;
932
- if (buildResult.status === 'no-valid-txs' || !forceCreate && numTxs < minValidTxs) {
933
- this.log.warn(`Block ${blockNumber} at index ${indexWithinCheckpoint} on slot ${this.slot} has too few valid txs to be proposed`, {
934
- slot: this.slot,
981
+ if (buildResult.status === 'insufficient-valid-txs') {
982
+ this.log.warn(`Block ${blockNumber} at index ${indexWithinCheckpoint} on slot ${this.targetSlot} has too few valid txs to be proposed`, {
983
+ slot: this.targetSlot,
935
984
  blockNumber,
936
- numTxs,
985
+ numTxs: buildResult.processedCount,
937
986
  indexWithinCheckpoint,
938
- minValidTxs,
939
- buildResult: buildResult.status
987
+ minValidTxs
940
988
  });
941
989
  this.eventEmitter.emit('block-build-failed', {
942
990
  reason: `Insufficient valid txs`,
943
- slot: this.slot
991
+ slot: this.targetSlot
944
992
  });
945
993
  this.metrics.recordBlockProposalFailed('insufficient_valid_txs');
946
994
  return undefined;
947
995
  }
948
996
  // Block creation succeeded, emit stats and metrics
949
- const { publicGas, block, publicProcessorDuration, usedTxs, usedTxBlobFields, blockBuildDuration } = buildResult;
997
+ const { block, publicProcessorDuration, usedTxs, blockBuildDuration, numTxs } = buildResult;
950
998
  const blockStats = {
951
999
  eventName: 'l2-block-built',
952
1000
  duration: blockBuildDuration,
@@ -955,8 +1003,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
955
1003
  };
956
1004
  const blockHash = await block.hash();
957
1005
  const txHashes = block.body.txEffects.map((tx)=>tx.txHash);
958
- const manaPerSec = publicGas.l2Gas / (blockBuildDuration / 1000);
959
- this.log.info(`Built block ${block.number} at checkpoint ${this.checkpointNumber} for slot ${this.slot} with ${numTxs} txs`, {
1006
+ const manaPerSec = block.header.totalManaUsed.toNumberUnsafe() / (blockBuildDuration / 1000);
1007
+ this.log.info(`Built block ${block.number} at checkpoint ${this.checkpointNumber} for slot ${this.targetSlot} with ${numTxs} txs`, {
960
1008
  blockHash,
961
1009
  txHashes,
962
1010
  manaPerSec,
@@ -964,22 +1012,22 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
964
1012
  });
965
1013
  this.eventEmitter.emit('block-proposed', {
966
1014
  blockNumber: block.number,
967
- slot: this.slot
1015
+ slot: this.targetSlot,
1016
+ buildSlot: this.slotNow
968
1017
  });
969
- this.metrics.recordBuiltBlock(blockBuildDuration, publicGas.l2Gas);
1018
+ this.metrics.recordBuiltBlock(blockBuildDuration, block.header.totalManaUsed.toNumberUnsafe());
970
1019
  return {
971
1020
  block,
972
- usedTxs,
973
- remainingBlobFields: maxBlobFieldsForTxs - usedTxBlobFields
1021
+ usedTxs
974
1022
  };
975
1023
  } catch (err) {
976
1024
  this.eventEmitter.emit('block-build-failed', {
977
1025
  reason: err.message,
978
- slot: this.slot
1026
+ slot: this.targetSlot
979
1027
  });
980
1028
  this.log.error(`Error building block`, err, {
981
1029
  blockNumber,
982
- slot: this.slot
1030
+ slot: this.targetSlot
983
1031
  });
984
1032
  this.metrics.recordBlockProposalFailed(err.name || 'unknown_error');
985
1033
  this.metrics.recordFailedBlock();
@@ -988,7 +1036,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
988
1036
  };
989
1037
  }
990
1038
  }
991
- /** Uses the checkpoint builder to build a block, catching specific txs */ async buildSingleBlockWithCheckpointBuilder(checkpointBuilder, pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions) {
1039
+ /** Uses the checkpoint builder to build a block, catching InsufficientValidTxsError. */ async buildSingleBlockWithCheckpointBuilder(checkpointBuilder, pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions) {
992
1040
  try {
993
1041
  const workTimer = new Timer();
994
1042
  const result = await checkpointBuilder.buildBlock(pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
@@ -999,10 +1047,11 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
999
1047
  status: 'success'
1000
1048
  };
1001
1049
  } catch (err) {
1002
- if (isErrorClass(err, NoValidTxsError)) {
1050
+ if (isErrorClass(err, InsufficientValidTxsError)) {
1003
1051
  return {
1004
1052
  failedTxs: err.failedTxs,
1005
- status: 'no-valid-txs'
1053
+ processedCount: err.processedCount,
1054
+ status: 'insufficient-valid-txs'
1006
1055
  };
1007
1056
  }
1008
1057
  throw err;
@@ -1021,14 +1070,15 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1021
1070
  if (startBuildingDeadline === undefined || now >= startBuildingDeadline) {
1022
1071
  return {
1023
1072
  canStartBuilding: false,
1024
- availableTxs: availableTxs
1073
+ availableTxs,
1074
+ minTxs
1025
1075
  };
1026
1076
  }
1027
1077
  // Wait a bit before checking again
1028
- this.setStateFn(SequencerState.WAITING_FOR_TXS, this.slot);
1029
- this.log.verbose(`Waiting for enough txs to build block ${blockNumber} at index ${indexWithinCheckpoint} in slot ${this.slot} (have ${availableTxs} but need ${minTxs})`, {
1078
+ this.setStateFn(SequencerState.WAITING_FOR_TXS, this.targetSlot);
1079
+ this.log.verbose(`Waiting for enough txs to build block ${blockNumber} at index ${indexWithinCheckpoint} in slot ${this.targetSlot} (have ${availableTxs} but need ${minTxs})`, {
1030
1080
  blockNumber,
1031
- slot: this.slot,
1081
+ slot: this.targetSlot,
1032
1082
  indexWithinCheckpoint
1033
1083
  });
1034
1084
  await this.waitForTxsPollingInterval();
@@ -1036,7 +1086,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1036
1086
  }
1037
1087
  return {
1038
1088
  canStartBuilding: true,
1039
- availableTxs
1089
+ availableTxs,
1090
+ minTxs
1040
1091
  };
1041
1092
  }
1042
1093
  /**
@@ -1181,16 +1232,16 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1181
1232
  /** Runs fee analysis and logs checkpoint outcome as fisherman */ async handleCheckpointEndAsFisherman(checkpoint) {
1182
1233
  // Perform L1 fee analysis before clearing requests
1183
1234
  // The callback is invoked asynchronously after the next block is mined
1184
- const feeAnalysis = await this.publisher.analyzeL1Fees(this.slot, (analysis)=>this.metrics.recordFishermanFeeAnalysis(analysis));
1235
+ const feeAnalysis = await this.publisher.analyzeL1Fees(this.targetSlot, (analysis)=>this.metrics.recordFishermanFeeAnalysis(analysis));
1185
1236
  if (checkpoint) {
1186
- this.log.info(`Validation checkpoint building SUCCEEDED for slot ${this.slot}`, {
1237
+ this.log.info(`Validation checkpoint building SUCCEEDED for slot ${this.targetSlot}`, {
1187
1238
  ...checkpoint.toCheckpointInfo(),
1188
1239
  ...checkpoint.getStats(),
1189
1240
  feeAnalysisId: feeAnalysis?.id
1190
1241
  });
1191
1242
  } else {
1192
- this.log.warn(`Validation block building FAILED for slot ${this.slot}`, {
1193
- slot: this.slot,
1243
+ this.log.warn(`Validation block building FAILED for slot ${this.targetSlot}`, {
1244
+ slot: this.targetSlot,
1194
1245
  feeAnalysisId: feeAnalysis?.id
1195
1246
  });
1196
1247
  this.metrics.recordCheckpointProposalFailed('block_build_failed');
@@ -1201,15 +1252,15 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1201
1252
  * Helper to handle HA double-signing errors. Returns true if the error was handled (caller should yield).
1202
1253
  */ handleHASigningError(err, errorContext) {
1203
1254
  if (err instanceof DutyAlreadySignedError) {
1204
- this.log.info(`${errorContext} for slot ${this.slot} already signed by another HA node, yielding`, {
1205
- slot: this.slot,
1255
+ this.log.info(`${errorContext} for slot ${this.targetSlot} already signed by another HA node, yielding`, {
1256
+ slot: this.targetSlot,
1206
1257
  signedByNode: err.signedByNode
1207
1258
  });
1208
1259
  return true;
1209
1260
  }
1210
1261
  if (err instanceof SlashingProtectionError) {
1211
- this.log.info(`${errorContext} for slot ${this.slot} blocked by slashing protection, yielding`, {
1212
- slot: this.slot,
1262
+ this.log.info(`${errorContext} for slot ${this.targetSlot} blocked by slashing protection, yielding`, {
1263
+ slot: this.targetSlot,
1213
1264
  existingMessageHash: err.existingMessageHash,
1214
1265
  attemptedMessageHash: err.attemptedMessageHash
1215
1266
  });
@@ -24,6 +24,7 @@ export type SequencerEvents = {
24
24
  ['block-proposed']: (args: {
25
25
  blockNumber: BlockNumber;
26
26
  slot: SlotNumber;
27
+ buildSlot: SlotNumber;
27
28
  }) => void;
28
29
  ['checkpoint-empty']: (args: {
29
30
  slot: SlotNumber;
@@ -43,4 +44,4 @@ export type SequencerEvents = {
43
44
  error: Error;
44
45
  }) => void;
45
46
  };
46
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL2V2ZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFakcsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDbEUsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRWpELE1BQU0sTUFBTSxlQUFlLEdBQUc7SUFDNUIsQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUN4QixRQUFRLEVBQUUsY0FBYyxDQUFDO1FBQ3pCLFFBQVEsRUFBRSxjQUFjLENBQUM7UUFDekIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxFQUFFLFVBQVUsQ0FBQztLQUNuQixLQUFLLElBQUksQ0FBQztJQUNYLENBQUMsOEJBQThCLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLE1BQU0sRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7SUFDdkYsQ0FBQyw2QkFBNkIsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztRQUFDLFlBQVksRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7SUFDNUcsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztRQUFDLElBQUksRUFBRSxVQUFVLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztJQUM3RSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFBRSxXQUFXLEVBQUUsV0FBVyxDQUFDO1FBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQTtLQUFFLEtBQUssSUFBSSxDQUFDO0lBQ25GLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLElBQUksRUFBRSxVQUFVLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztJQUMzRCxDQUFDLDJCQUEyQixDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFDcEMsSUFBSSxFQUFFLFVBQVUsQ0FBQztRQUNqQixpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQzdCLGFBQWEsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3pCLFdBQVcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3ZCLGNBQWMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO0tBQzNCLEtBQUssSUFBSSxDQUFDO0lBQ1gsQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQUUsVUFBVSxFQUFFLGdCQUFnQixDQUFDO1FBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQTtLQUFFLEtBQUssSUFBSSxDQUFDO0lBQzdGLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLEtBQUssRUFBRSxLQUFLLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztDQUN4RCxDQUFDIn0=
47
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL2V2ZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFakcsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDbEUsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRWpELE1BQU0sTUFBTSxlQUFlLEdBQUc7SUFDNUIsQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUN4QixRQUFRLEVBQUUsY0FBYyxDQUFDO1FBQ3pCLFFBQVEsRUFBRSxjQUFjLENBQUM7UUFDekIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxFQUFFLFVBQVUsQ0FBQztLQUNuQixLQUFLLElBQUksQ0FBQztJQUNYLENBQUMsOEJBQThCLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLE1BQU0sRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7SUFDdkYsQ0FBQyw2QkFBNkIsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztRQUFDLFlBQVksRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7SUFDNUcsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztRQUFDLElBQUksRUFBRSxVQUFVLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztJQUM3RSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFBRSxXQUFXLEVBQUUsV0FBVyxDQUFDO1FBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQztRQUFDLFNBQVMsRUFBRSxVQUFVLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztJQUMxRyxDQUFDLGtCQUFrQixDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFBRSxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7SUFDM0QsQ0FBQywyQkFBMkIsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFO1FBQ3BDLElBQUksRUFBRSxVQUFVLENBQUM7UUFDakIsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUM3QixhQUFhLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUN6QixXQUFXLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUN2QixjQUFjLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztLQUMzQixLQUFLLElBQUksQ0FBQztJQUNYLENBQUMsc0JBQXNCLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQztRQUFDLElBQUksRUFBRSxVQUFVLENBQUE7S0FBRSxLQUFLLElBQUksQ0FBQztJQUM3RixDQUFDLGtCQUFrQixDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFBO0tBQUUsS0FBSyxJQUFJLENBQUM7Q0FDeEQsQ0FBQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/sequencer/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAEjG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE;QACxB,QAAQ,EAAE,cAAc,CAAC;QACzB,QAAQ,EAAE,cAAc,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,IAAI,CAAC,EAAE,UAAU,CAAC;KACnB,KAAK,IAAI,CAAC;IACX,CAAC,8BAA8B,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IACvF,CAAC,6BAA6B,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5G,CAAC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,CAAC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,CAAC,2BAA2B,CAAC,EAAE,CAAC,IAAI,EAAE;QACpC,IAAI,EAAE,UAAU,CAAC;QACjB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX,CAAC,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,gBAAgB,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7F,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAC;CACxD,CAAC"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/sequencer/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAEjG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE;QACxB,QAAQ,EAAE,cAAc,CAAC;QACzB,QAAQ,EAAE,cAAc,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,IAAI,CAAC,EAAE,UAAU,CAAC;KACnB,KAAK,IAAI,CAAC;IACX,CAAC,8BAA8B,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IACvF,CAAC,6BAA6B,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5G,CAAC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,CAAC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1G,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,CAAC,2BAA2B,CAAC,EAAE,CAAC,IAAI,EAAE;QACpC,IAAI,EAAE,UAAU,CAAC;QACjB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX,CAAC,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,gBAAgB,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7F,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAC;CACxD,CAAC"}