@aztec/sequencer-client 0.0.1-commit.9372f48 → 0.0.1-commit.967fc6998

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 (70) hide show
  1. package/dest/client/sequencer-client.d.ts +12 -7
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +15 -4
  4. package/dest/config.d.ts +3 -4
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +17 -12
  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/publisher/config.d.ts +35 -17
  10. package/dest/publisher/config.d.ts.map +1 -1
  11. package/dest/publisher/config.js +106 -42
  12. package/dest/publisher/index.d.ts +2 -1
  13. package/dest/publisher/index.d.ts.map +1 -1
  14. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  15. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  16. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  17. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  18. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  19. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  20. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  21. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  22. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  23. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  24. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  25. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  26. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
  27. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  28. package/dest/publisher/sequencer-publisher-factory.js +13 -2
  29. package/dest/publisher/sequencer-publisher.d.ts +22 -7
  30. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  31. package/dest/publisher/sequencer-publisher.js +258 -29
  32. package/dest/sequencer/checkpoint_proposal_job.d.ts +7 -1
  33. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  34. package/dest/sequencer/checkpoint_proposal_job.js +67 -40
  35. package/dest/sequencer/metrics.d.ts +17 -5
  36. package/dest/sequencer/metrics.d.ts.map +1 -1
  37. package/dest/sequencer/metrics.js +86 -15
  38. package/dest/sequencer/sequencer.d.ts +17 -7
  39. package/dest/sequencer/sequencer.d.ts.map +1 -1
  40. package/dest/sequencer/sequencer.js +29 -26
  41. package/dest/sequencer/timetable.d.ts +1 -4
  42. package/dest/sequencer/timetable.d.ts.map +1 -1
  43. package/dest/sequencer/timetable.js +2 -5
  44. package/dest/test/index.d.ts +3 -5
  45. package/dest/test/index.d.ts.map +1 -1
  46. package/dest/test/mock_checkpoint_builder.d.ts +5 -3
  47. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  48. package/dest/test/mock_checkpoint_builder.js +6 -4
  49. package/dest/test/utils.d.ts +3 -3
  50. package/dest/test/utils.d.ts.map +1 -1
  51. package/dest/test/utils.js +5 -4
  52. package/package.json +28 -28
  53. package/src/client/sequencer-client.ts +25 -7
  54. package/src/config.ts +26 -19
  55. package/src/global_variable_builder/global_builder.ts +1 -1
  56. package/src/publisher/config.ts +121 -43
  57. package/src/publisher/index.ts +3 -0
  58. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  59. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  60. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  61. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  62. package/src/publisher/sequencer-publisher-factory.ts +23 -6
  63. package/src/publisher/sequencer-publisher.ts +241 -36
  64. package/src/sequencer/checkpoint_proposal_job.ts +91 -39
  65. package/src/sequencer/metrics.ts +92 -18
  66. package/src/sequencer/sequencer.ts +39 -31
  67. package/src/sequencer/timetable.ts +7 -6
  68. package/src/test/index.ts +2 -4
  69. package/src/test/mock_checkpoint_builder.ts +12 -1
  70. package/src/test/utils.ts +5 -2
@@ -451,7 +451,7 @@ import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
451
451
  import { Gas } from '@aztec/stdlib/gas';
452
452
  import { NoValidTxsError } from '@aztec/stdlib/interfaces/server';
453
453
  import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
454
- import { orderAttestations } from '@aztec/stdlib/p2p';
454
+ import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p';
455
455
  import { AttestationTimeoutError } from '@aztec/stdlib/validators';
456
456
  import { Attributes, trackSpan } from '@aztec/telemetry-client';
457
457
  import { DutyAlreadySignedError, SlashingProtectionError } from '@aztec/validator-ha-signer/errors';
@@ -590,7 +590,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
590
590
  // Wait until the voting promises have resolved, so all requests are enqueued (not sent)
591
591
  await Promise.all(votesPromises);
592
592
  if (checkpoint) {
593
- this.metrics.recordBlockProposalSuccess();
593
+ this.metrics.recordCheckpointProposalSuccess();
594
594
  }
595
595
  // Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
596
596
  if (this.config.fishermanMode) {
@@ -640,13 +640,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
640
640
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
641
641
  const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
642
642
  // Collect the out hashes of all the checkpoints before this one in the same epoch
643
- const previousCheckpoints = (await this.l2BlockSource.getCheckpointsForEpoch(this.epoch)).filter((c)=>c.number < this.checkpointNumber);
644
- const previousCheckpointOutHashes = previousCheckpoints.map((c)=>c.getCheckpointOutHash());
643
+ const previousCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(this.epoch)).filter((c)=>c.checkpointNumber < this.checkpointNumber).map((c)=>c.checkpointOutHash);
644
+ // Get the fee asset price modifier from the oracle
645
+ const feeAssetPriceModifier = await this.publisher.getFeeAssetPriceModifier();
645
646
  const fork = _ts_add_disposable_resource(env, await this.worldState.fork(this.syncedToBlockNumber, {
646
647
  closeDelayMs: 12_000
647
- }), false);
648
+ }), true);
648
649
  // Create checkpoint builder for the entire slot
649
- const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(this.checkpointNumber, checkpointGlobalVariables, l1ToL2Messages, previousCheckpointOutHashes, fork, this.log.getBindings());
650
+ const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(this.checkpointNumber, checkpointGlobalVariables, feeAssetPriceModifier, l1ToL2Messages, previousCheckpointOutHashes, fork, this.log.getBindings());
650
651
  // Options for the validator client when creating block and checkpoint proposals
651
652
  const blockProposalOptions = {
652
653
  publishFullTxs: !!this.config.publishTxsWithProposals,
@@ -658,6 +659,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
658
659
  };
659
660
  let blocksInCheckpoint = [];
660
661
  let blockPendingBroadcast = undefined;
662
+ const checkpointBuildTimer = new Timer();
661
663
  try {
662
664
  // Main loop: build blocks for the checkpoint
663
665
  const result = await this.buildBlocksForCheckpoint(checkpointBuilder, checkpointGlobalVariables.timestamp, inHash, blockProposalOptions);
@@ -667,19 +669,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
667
669
  // These errors are expected in HA mode, so we yield and let another HA node handle the slot
668
670
  // The only distinction between the 2 errors is SlashingProtectionError throws when the payload is different,
669
671
  // which is normal for block building (may have picked different txs)
670
- if (err instanceof DutyAlreadySignedError) {
671
- this.log.info(`Checkpoint proposal for slot ${this.slot} already signed by another HA node, yielding`, {
672
- slot: this.slot,
673
- signedByNode: err.signedByNode
674
- });
675
- return undefined;
676
- }
677
- if (err instanceof SlashingProtectionError) {
678
- this.log.info(`Checkpoint proposal for slot ${this.slot} blocked by slashing protection, yielding`, {
679
- slot: this.slot,
680
- existingMessageHash: err.existingMessageHash,
681
- attemptedMessageHash: err.attemptedMessageHash
682
- });
672
+ if (this.handleHASigningError(err, 'Block proposal')) {
683
673
  return undefined;
684
674
  }
685
675
  throw err;
@@ -693,10 +683,21 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
693
683
  });
694
684
  return undefined;
695
685
  }
686
+ const minBlocksForCheckpoint = this.config.minBlocksForCheckpoint;
687
+ if (minBlocksForCheckpoint !== undefined && blocksInCheckpoint.length < minBlocksForCheckpoint) {
688
+ this.log.warn(`Checkpoint has fewer blocks than minimum (${blocksInCheckpoint.length} < ${minBlocksForCheckpoint}), skipping proposal`, {
689
+ slot: this.slot,
690
+ blocksBuilt: blocksInCheckpoint.length,
691
+ minBlocksForCheckpoint
692
+ });
693
+ return undefined;
694
+ }
696
695
  // Assemble and broadcast the checkpoint proposal, including the last block that was not
697
696
  // broadcasted yet, and wait to collect the committee attestations.
698
697
  this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.slot);
699
698
  const checkpoint = await checkpointBuilder.completeCheckpoint();
699
+ // Record checkpoint-level build metrics
700
+ this.metrics.recordCheckpointBuild(checkpointBuildTimer.ms(), blocksInCheckpoint.length, checkpoint.getStats().txCount, Number(checkpoint.header.totalManaUsed.toBigInt()));
700
701
  // Do not collect attestations nor publish to L1 in fisherman mode
701
702
  if (this.config.fishermanMode) {
702
703
  this.log.info(`Built checkpoint for slot ${this.slot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
@@ -714,7 +715,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
714
715
  txs: blockPendingBroadcast.txs
715
716
  };
716
717
  // Create the checkpoint proposal and broadcast it
717
- const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, lastBlock, this.proposer, checkpointProposalOptions);
718
+ const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier, lastBlock, this.proposer, checkpointProposalOptions);
718
719
  const blockProposedAt = this.dateProvider.now();
719
720
  await this.p2pClient.broadcastCheckpointProposal(proposal);
720
721
  this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.slot);
@@ -728,20 +729,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
728
729
  attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.slot, this.checkpointNumber);
729
730
  } catch (err) {
730
731
  // We shouldn't really get here since we yield to another HA node
731
- // as soon as we see these errors when creating block proposals.
732
- if (err instanceof DutyAlreadySignedError) {
733
- this.log.info(`Attestations signature for slot ${this.slot} already signed by another HA node, yielding`, {
734
- slot: this.slot,
735
- signedByNode: err.signedByNode
736
- });
737
- return undefined;
738
- }
739
- if (err instanceof SlashingProtectionError) {
740
- this.log.info(`Attestations signature for slot ${this.slot} blocked by slashing protection, yielding`, {
741
- slot: this.slot,
742
- existingMessageHash: err.existingMessageHash,
743
- attemptedMessageHash: err.attemptedMessageHash
744
- });
732
+ // as soon as we see these errors when creating block or checkpoint proposals.
733
+ if (this.handleHASigningError(err, 'Attestations signature')) {
745
734
  return undefined;
746
735
  }
747
736
  throw err;
@@ -751,6 +740,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
751
740
  const aztecSlotDuration = this.l1Constants.slotDuration;
752
741
  const slotStartBuildTimestamp = this.getSlotStartBuildTimestamp();
753
742
  const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
743
+ // If we have been configured to potentially skip publishing checkpoint then roll the dice here
744
+ if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
745
+ const result = Math.max(0, randomInt(100));
746
+ if (result < this.config.skipPublishingCheckpointsPercent) {
747
+ this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${result}`);
748
+ return checkpoint;
749
+ }
750
+ }
754
751
  await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
755
752
  txTimeoutAt,
756
753
  forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber
@@ -760,7 +757,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
760
757
  env.error = e;
761
758
  env.hasError = true;
762
759
  } finally{
763
- _ts_dispose_resources(env);
760
+ const result = _ts_dispose_resources(env);
761
+ if (result) await result;
764
762
  }
765
763
  } catch (err) {
766
764
  if (err && (err instanceof DutyAlreadySignedError || err instanceof SlashingProtectionError)) {
@@ -906,7 +904,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
906
904
  }
907
905
  // Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
908
906
  // just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
909
- const pendingTxs = filter(this.p2pClient.iteratePendingTxs(), (tx)=>!txHashesAlreadyIncluded.has(tx.txHash.toString()));
907
+ const pendingTxs = filter(this.p2pClient.iterateEligiblePendingTxs(), (tx)=>!txHashesAlreadyIncluded.has(tx.txHash.toString()));
910
908
  this.log.debug(`Building block ${blockNumber} at index ${indexWithinCheckpoint} for slot ${this.slot} with ${availableTxs} available txs`, {
911
909
  slot: this.slot,
912
910
  blockNumber,
@@ -1033,7 +1031,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1033
1031
  slot: this.slot,
1034
1032
  indexWithinCheckpoint
1035
1033
  });
1036
- await sleep(TXS_POLLING_MS);
1034
+ await this.waitForTxsPollingInterval();
1037
1035
  availableTxs = await this.p2pClient.getPendingTxCount();
1038
1036
  }
1039
1037
  return {
@@ -1075,8 +1073,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1075
1073
  try {
1076
1074
  const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
1077
1075
  collectedAttestationsCount = attestations.length;
1076
+ // Trim attestations to minimum required to save L1 calldata gas
1077
+ const localAddresses = this.validatorClient.getValidatorAddresses();
1078
+ const trimmed = trimAttestations(attestations, numberOfRequiredAttestations, this.attestorAddress, localAddresses);
1079
+ if (trimmed.length < attestations.length) {
1080
+ this.log.debug(`Trimmed attestations from ${attestations.length} to ${trimmed.length} for L1 submission`);
1081
+ }
1078
1082
  // Rollup contract requires that the signatures are provided in the order of the committee
1079
- const sorted = orderAttestations(attestations, committee);
1083
+ const sorted = orderAttestations(trimmed, committee);
1080
1084
  // Manipulate the attestations if we've been configured to do so
1081
1085
  if (this.config.injectFakeAttestation || this.config.shuffleAttestationOrdering) {
1082
1086
  return this.manipulateAttestations(proposal.slotNumber, epoch, seed, committee, sorted);
@@ -1135,7 +1139,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1135
1139
  const failedTxData = failedTxs.map((fail)=>fail.tx);
1136
1140
  const failedTxHashes = failedTxData.map((tx)=>tx.getTxHash());
1137
1141
  this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
1138
- await this.p2pClient.deleteTxs(failedTxHashes);
1142
+ await this.p2pClient.handleFailedExecution(failedTxHashes);
1139
1143
  }
1140
1144
  /**
1141
1145
  * Adds the proposed block to the archiver so it's available via P2P.
@@ -1170,15 +1174,38 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
1170
1174
  slot: this.slot,
1171
1175
  feeAnalysisId: feeAnalysis?.id
1172
1176
  });
1173
- this.metrics.recordBlockProposalFailed('block_build_failed');
1177
+ this.metrics.recordCheckpointProposalFailed('block_build_failed');
1174
1178
  }
1175
1179
  this.publisher.clearPendingRequests();
1176
1180
  }
1181
+ /**
1182
+ * Helper to handle HA double-signing errors. Returns true if the error was handled (caller should yield).
1183
+ */ handleHASigningError(err, errorContext) {
1184
+ if (err instanceof DutyAlreadySignedError) {
1185
+ this.log.info(`${errorContext} for slot ${this.slot} already signed by another HA node, yielding`, {
1186
+ slot: this.slot,
1187
+ signedByNode: err.signedByNode
1188
+ });
1189
+ return true;
1190
+ }
1191
+ if (err instanceof SlashingProtectionError) {
1192
+ this.log.info(`${errorContext} for slot ${this.slot} blocked by slashing protection, yielding`, {
1193
+ slot: this.slot,
1194
+ existingMessageHash: err.existingMessageHash,
1195
+ attemptedMessageHash: err.attemptedMessageHash
1196
+ });
1197
+ return true;
1198
+ }
1199
+ return false;
1200
+ }
1177
1201
  /** Waits until a specific time within the current slot */ async waitUntilTimeInSlot(targetSecondsIntoSlot) {
1178
1202
  const slotStartTimestamp = this.getSlotStartBuildTimestamp();
1179
1203
  const targetTimestamp = slotStartTimestamp + targetSecondsIntoSlot;
1180
1204
  await sleepUntil(new Date(targetTimestamp * 1000), this.dateProvider.nowAsDate());
1181
1205
  }
1206
+ /** Waits the polling interval for transactions. Extracted for test overriding. */ async waitForTxsPollingInterval() {
1207
+ await sleep(TXS_POLLING_MS);
1208
+ }
1182
1209
  getSlotStartBuildTimestamp() {
1183
1210
  return getSlotStartBuildTimestamp(this.slot, this.l1Constants);
1184
1211
  }
@@ -21,15 +21,24 @@ export declare class SequencerMetrics {
21
21
  private slots;
22
22
  private filledSlots;
23
23
  private blockProposalFailed;
24
- private blockProposalSuccess;
25
- private blockProposalPrecheckFailed;
24
+ private checkpointProposalSuccess;
25
+ private checkpointPrecheckFailed;
26
+ private checkpointProposalFailed;
26
27
  private checkpointSuccess;
27
28
  private slashingAttempts;
28
29
  private checkpointAttestationDelay;
30
+ private checkpointBuildDuration;
31
+ private checkpointBlockCount;
32
+ private checkpointTxCount;
33
+ private checkpointTotalMana;
29
34
  private fishermanWouldBeIncluded;
30
35
  private fishermanTimeBeforeBlock;
31
36
  private fishermanPendingBlobTxCount;
32
37
  private fishermanIncludedBlobTxCount;
38
+ private fishermanPendingBlobCount;
39
+ private fishermanIncludedBlobCount;
40
+ private fishermanBlockBlobsFull;
41
+ private fishermanMaxBlobCapacity;
33
42
  private fishermanCalculatedPriorityFee;
34
43
  private fishermanPriorityFeeDelta;
35
44
  private fishermanEstimatedCost;
@@ -48,8 +57,11 @@ export declare class SequencerMetrics {
48
57
  incFilledSlot(proposer: string, coinbase: Hex | EthAddress | undefined): Promise<void>;
49
58
  recordCheckpointSuccess(): void;
50
59
  recordBlockProposalFailed(reason?: string): void;
51
- recordBlockProposalSuccess(): void;
52
- recordBlockProposalPrecheckFailed(checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch'): void;
60
+ recordCheckpointProposalSuccess(): void;
61
+ recordCheckpointPrecheckFailed(checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch'): void;
62
+ recordCheckpointProposalFailed(reason?: string): void;
63
+ /** Records aggregate metrics for a completed checkpoint build. */
64
+ recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number): void;
53
65
  recordSlashingAttempt(actionCount: number): void;
54
66
  /**
55
67
  * Records metrics for a completed fisherman fee analysis
@@ -57,4 +69,4 @@ export declare class SequencerMetrics {
57
69
  */
58
70
  recordFishermanFeeAnalysis(analysis: L1FeeAnalysisResult): void;
59
71
  }
60
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9tZXRyaWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUN2RCxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzNFLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2xFLE9BQU8sRUFNTCxLQUFLLGVBQWUsRUFDcEIsS0FBSyxNQUFNLEVBR1osTUFBTSx5QkFBeUIsQ0FBQztBQUVqQyxPQUFPLEVBQUUsS0FBSyxHQUFHLEVBQWUsTUFBTSxNQUFNLENBQUM7QUFFN0MsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBR2pELHFCQUFhLGdCQUFnQjtJQTJDekIsT0FBTyxDQUFDLE1BQU07SUExQ2hCLFNBQWdCLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFDL0IsT0FBTyxDQUFDLEtBQUssQ0FBUTtJQUVyQixPQUFPLENBQUMsWUFBWSxDQUFnQjtJQUNwQyxPQUFPLENBQUMsa0JBQWtCLENBQVk7SUFDdEMsT0FBTyxDQUFDLHVCQUF1QixDQUFRO0lBQ3ZDLE9BQU8sQ0FBQyw2QkFBNkIsQ0FBWTtJQUdqRCxPQUFPLENBQUMseUJBQXlCLENBQVE7SUFDekMsT0FBTyxDQUFDLDhCQUE4QixDQUFRO0lBQzlDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBUTtJQUNsQyxPQUFPLENBQUMsbUJBQW1CLENBQVE7SUFFbkMsT0FBTyxDQUFDLE9BQU8sQ0FBUTtJQUV2QixPQUFPLENBQUMsS0FBSyxDQUFnQjtJQUM3QixPQUFPLENBQUMsV0FBVyxDQUFnQjtJQUVuQyxPQUFPLENBQUMsbUJBQW1CLENBQWdCO0lBQzNDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBZ0I7SUFDNUMsT0FBTyxDQUFDLDJCQUEyQixDQUFnQjtJQUNuRCxPQUFPLENBQUMsaUJBQWlCLENBQWdCO0lBQ3pDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBZ0I7SUFDeEMsT0FBTyxDQUFDLDBCQUEwQixDQUFZO0lBRzlDLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBZ0I7SUFDaEQsT0FBTyxDQUFDLHdCQUF3QixDQUFZO0lBQzVDLE9BQU8sQ0FBQywyQkFBMkIsQ0FBWTtJQUMvQyxPQUFPLENBQUMsNEJBQTRCLENBQVk7SUFDaEQsT0FBTyxDQUFDLDhCQUE4QixDQUFZO0lBQ2xELE9BQU8sQ0FBQyx5QkFBeUIsQ0FBWTtJQUM3QyxPQUFPLENBQUMsc0JBQXNCLENBQVk7SUFDMUMsT0FBTyxDQUFDLDZCQUE2QixDQUFZO0lBQ2pELE9BQU8sQ0FBQywrQkFBK0IsQ0FBWTtJQUNuRCxPQUFPLENBQUMsNkJBQTZCLENBQVk7SUFFakQsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFhO0lBRWxDLFlBQ0UsTUFBTSxFQUFFLGVBQWUsRUFDZixNQUFNLEVBQUUsY0FBYyxFQUM5QixJQUFJLFNBQWMsRUFrR25CO0lBRU0sMEJBQTBCLENBQUMseUJBQXlCLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxNQUFNLFFBT3ZGO0lBRU0sZ0NBQWdDLENBQUMsUUFBUSxFQUFFLE1BQU0sUUFFdkQ7SUFFTSwyQkFBMkIsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLFFBR25FO0lBRUQsZ0JBQWdCLENBQUMsZUFBZSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxRQU0xRDtJQUVELGlCQUFpQixTQUloQjtJQUVELDZCQUE2QixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLGNBQWMsUUFJdEU7SUFFRCxXQUFXLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsTUFBTSxRQVc3QztJQUVLLGFBQWEsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLEdBQUcsVUFBVSxHQUFHLFNBQVMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBaUIzRjtJQUVELHVCQUF1QixTQUV0QjtJQUVELHlCQUF5QixDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sUUFJeEM7SUFFRCwwQkFBMEIsU0FFekI7SUFFRCxpQ0FBaUMsQ0FDL0IsU0FBUyxFQUFFLG9CQUFvQixHQUFHLDhCQUE4QixHQUFHLGVBQWUsR0FBRyx1QkFBdUIsUUFLN0c7SUFFRCxxQkFBcUIsQ0FBQyxXQUFXLEVBQUUsTUFBTSxRQUV4QztJQUVEOzs7T0FHRztJQUNILDBCQUEwQixDQUFDLFFBQVEsRUFBRSxtQkFBbUIsUUFxRnZEO0NBQ0YifQ==
72
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9tZXRyaWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUN2RCxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzNFLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2xFLE9BQU8sRUFNTCxLQUFLLGVBQWUsRUFDcEIsS0FBSyxNQUFNLEVBR1osTUFBTSx5QkFBeUIsQ0FBQztBQUVqQyxPQUFPLEVBQUUsS0FBSyxHQUFHLEVBQWUsTUFBTSxNQUFNLENBQUM7QUFFN0MsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRWpELHFCQUFhLGdCQUFnQjtJQW9EekIsT0FBTyxDQUFDLE1BQU07SUFuRGhCLFNBQWdCLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFDL0IsT0FBTyxDQUFDLEtBQUssQ0FBUTtJQUVyQixPQUFPLENBQUMsWUFBWSxDQUFnQjtJQUNwQyxPQUFPLENBQUMsa0JBQWtCLENBQVk7SUFDdEMsT0FBTyxDQUFDLHVCQUF1QixDQUFRO0lBQ3ZDLE9BQU8sQ0FBQyw2QkFBNkIsQ0FBWTtJQUdqRCxPQUFPLENBQUMseUJBQXlCLENBQVE7SUFDekMsT0FBTyxDQUFDLDhCQUE4QixDQUFRO0lBQzlDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBUTtJQUNsQyxPQUFPLENBQUMsbUJBQW1CLENBQVE7SUFFbkMsT0FBTyxDQUFDLE9BQU8sQ0FBUTtJQUV2QixPQUFPLENBQUMsS0FBSyxDQUFnQjtJQUM3QixPQUFPLENBQUMsV0FBVyxDQUFnQjtJQUVuQyxPQUFPLENBQUMsbUJBQW1CLENBQWdCO0lBQzND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
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/sequencer/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,MAAM,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,KAAK,GAAG,EAAe,MAAM,MAAM,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,qBAAa,gBAAgB;IA2CzB,OAAO,CAAC,MAAM;IA1ChB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,KAAK,CAAQ;IAErB,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,uBAAuB,CAAQ;IACvC,OAAO,CAAC,6BAA6B,CAAY;IAGjD,OAAO,CAAC,yBAAyB,CAAQ;IACzC,OAAO,CAAC,8BAA8B,CAAQ;IAC9C,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,mBAAmB,CAAQ;IAEnC,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,WAAW,CAAgB;IAEnC,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,2BAA2B,CAAgB;IACnD,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,0BAA0B,CAAY;IAG9C,OAAO,CAAC,wBAAwB,CAAgB;IAChD,OAAO,CAAC,wBAAwB,CAAY;IAC5C,OAAO,CAAC,2BAA2B,CAAY;IAC/C,OAAO,CAAC,4BAA4B,CAAY;IAChD,OAAO,CAAC,8BAA8B,CAAY;IAClD,OAAO,CAAC,yBAAyB,CAAY;IAC7C,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,6BAA6B,CAAY;IACjD,OAAO,CAAC,+BAA+B,CAAY;IACnD,OAAO,CAAC,6BAA6B,CAAY;IAEjD,OAAO,CAAC,YAAY,CAAC,CAAa;IAElC,YACE,MAAM,EAAE,eAAe,EACf,MAAM,EAAE,cAAc,EAC9B,IAAI,SAAc,EAkGnB;IAEM,0BAA0B,CAAC,yBAAyB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAOvF;IAEM,gCAAgC,CAAC,QAAQ,EAAE,MAAM,QAEvD;IAEM,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAGnE;IAED,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAM1D;IAED,iBAAiB,SAIhB;IAED,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,QAItE;IAED,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,QAW7C;IAEK,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3F;IAED,uBAAuB,SAEtB;IAED,yBAAyB,CAAC,MAAM,CAAC,EAAE,MAAM,QAIxC;IAED,0BAA0B,SAEzB;IAED,iCAAiC,CAC/B,SAAS,EAAE,oBAAoB,GAAG,8BAA8B,GAAG,eAAe,GAAG,uBAAuB,QAK7G;IAED,qBAAqB,CAAC,WAAW,EAAE,MAAM,QAExC;IAED;;;OAGG;IACH,0BAA0B,CAAC,QAAQ,EAAE,mBAAmB,QAqFvD;CACF"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/sequencer/metrics.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  import { Attributes, Metrics, createUpDownCounterWithDefault } from '@aztec/telemetry-client';
2
2
  import { formatUnits } from 'viem';
3
- // TODO(palla/mbps): Review all metrics and add any missing ones per checkpoint
4
3
  export class SequencerMetrics {
5
4
  rollup;
6
5
  tracer;
@@ -18,16 +17,25 @@ export class SequencerMetrics {
18
17
  slots;
19
18
  filledSlots;
20
19
  blockProposalFailed;
21
- blockProposalSuccess;
22
- blockProposalPrecheckFailed;
20
+ checkpointProposalSuccess;
21
+ checkpointPrecheckFailed;
22
+ checkpointProposalFailed;
23
23
  checkpointSuccess;
24
24
  slashingAttempts;
25
25
  checkpointAttestationDelay;
26
+ checkpointBuildDuration;
27
+ checkpointBlockCount;
28
+ checkpointTxCount;
29
+ checkpointTotalMana;
26
30
  // Fisherman fee analysis metrics
27
31
  fishermanWouldBeIncluded;
28
32
  fishermanTimeBeforeBlock;
29
33
  fishermanPendingBlobTxCount;
30
34
  fishermanIncludedBlobTxCount;
35
+ fishermanPendingBlobCount;
36
+ fishermanIncludedBlobCount;
37
+ fishermanBlockBlobsFull;
38
+ fishermanMaxBlobCapacity;
31
39
  fishermanCalculatedPriorityFee;
32
40
  fishermanPriorityFeeDelta;
33
41
  fishermanEstimatedCost;
@@ -49,7 +57,7 @@ export class SequencerMetrics {
49
57
  this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
50
58
  this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
51
59
  this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
52
- this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS);
60
+ this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
53
61
  this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
54
62
  /**
55
63
  * NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
@@ -60,9 +68,9 @@ export class SequencerMetrics {
60
68
  this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT);
61
69
  this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
62
70
  this.blockProposalFailed = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT);
63
- this.blockProposalSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT);
71
+ this.checkpointProposalSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_SUCCESS_COUNT);
64
72
  this.checkpointSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
65
- this.blockProposalPrecheckFailed = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_PROPOSAL_PRECHECK_FAILED_COUNT, {
73
+ this.checkpointPrecheckFailed = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_PRECHECK_FAILED_COUNT, {
66
74
  [Attributes.ERROR_TYPE]: [
67
75
  'slot_already_taken',
68
76
  'rollup_contract_check_failed',
@@ -70,12 +78,21 @@ export class SequencerMetrics {
70
78
  'block_number_mismatch'
71
79
  ]
72
80
  });
81
+ this.checkpointProposalFailed = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT);
82
+ this.checkpointBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION);
83
+ this.checkpointBlockCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT);
84
+ this.checkpointTxCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT);
85
+ this.checkpointTotalMana = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA);
73
86
  this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
74
87
  // Fisherman fee analysis metrics
75
88
  this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(this.meter, Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED, {
76
89
  [Attributes.OK]: [
77
90
  true,
78
91
  false
92
+ ],
93
+ [Attributes.BLOCK_FULL]: [
94
+ 'true',
95
+ 'false'
79
96
  ]
80
97
  });
81
98
  this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
@@ -87,6 +104,15 @@ export class SequencerMetrics {
87
104
  this.fishermanEstimatedOverpayment = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_OVERPAYMENT);
88
105
  this.fishermanMinedBlobTxPriorityFee = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_PRIORITY_FEE);
89
106
  this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST);
107
+ this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
108
+ this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
109
+ this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(this.meter, Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL, {
110
+ [Attributes.OK]: [
111
+ true,
112
+ false
113
+ ]
114
+ });
115
+ this.fishermanMaxBlobCapacity = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MAX_BLOB_CAPACITY);
90
116
  }
91
117
  recordRequiredAttestations(requiredAttestationsCount, allowanceMs) {
92
118
  this.requiredAttestions.record(requiredAttestationsCount);
@@ -156,14 +182,27 @@ export class SequencerMetrics {
156
182
  }
157
183
  });
158
184
  }
159
- recordBlockProposalSuccess() {
160
- this.blockProposalSuccess.add(1);
185
+ recordCheckpointProposalSuccess() {
186
+ this.checkpointProposalSuccess.add(1);
161
187
  }
162
- recordBlockProposalPrecheckFailed(checkType) {
163
- this.blockProposalPrecheckFailed.add(1, {
188
+ recordCheckpointPrecheckFailed(checkType) {
189
+ this.checkpointPrecheckFailed.add(1, {
164
190
  [Attributes.ERROR_TYPE]: checkType
165
191
  });
166
192
  }
193
+ recordCheckpointProposalFailed(reason) {
194
+ this.checkpointProposalFailed.add(1, {
195
+ ...reason && {
196
+ [Attributes.ERROR_TYPE]: reason
197
+ }
198
+ });
199
+ }
200
+ /** Records aggregate metrics for a completed checkpoint build. */ recordCheckpointBuild(durationMs, blockCount, txCount, totalMana) {
201
+ this.checkpointBuildDuration.record(Math.ceil(durationMs));
202
+ this.checkpointBlockCount.record(blockCount);
203
+ this.checkpointTxCount.record(txCount);
204
+ this.checkpointTotalMana.record(totalMana);
205
+ }
167
206
  recordSlashingAttempt(actionCount) {
168
207
  this.slashingAttempts.add(actionCount);
169
208
  }
@@ -184,9 +223,11 @@ export class SequencerMetrics {
184
223
  };
185
224
  // Record pending block snapshot data (once per strategy for comparison)
186
225
  this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
226
+ this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
187
227
  // Record mined block data if available
188
228
  if (analysis.minedBlock) {
189
229
  this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
230
+ this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
190
231
  // Record actual fees from blob transactions in the mined block
191
232
  for (const blobTx of analysis.minedBlock.includedBlobTxs){
192
233
  // Record priority fee per gas in Gwei
@@ -211,16 +252,34 @@ export class SequencerMetrics {
211
252
  // Record analysis results if available
212
253
  if (analysis.analysis) {
213
254
  this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
255
+ // Record whether the block reached 100% blob capacity
256
+ if (analysis.analysis.blockBlobsFull) {
257
+ this.fishermanBlockBlobsFull.add(1, {
258
+ ...strategyAttributes,
259
+ [Attributes.OK]: true
260
+ });
261
+ } else {
262
+ this.fishermanBlockBlobsFull.add(1, {
263
+ ...strategyAttributes,
264
+ [Attributes.OK]: false
265
+ });
266
+ }
267
+ // Record the max blob capacity for this block
268
+ this.fishermanMaxBlobCapacity.record(analysis.analysis.maxBlobCapacity, strategyAttributes);
214
269
  // Record strategy-specific inclusion result
215
270
  if (strategyResult.wouldBeIncluded !== undefined) {
271
+ const inclusionAttributes = {
272
+ ...strategyAttributes,
273
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false'
274
+ };
216
275
  if (strategyResult.wouldBeIncluded) {
217
276
  this.fishermanWouldBeIncluded.add(1, {
218
- ...strategyAttributes,
277
+ ...inclusionAttributes,
219
278
  [Attributes.OK]: true
220
279
  });
221
280
  } else {
222
281
  this.fishermanWouldBeIncluded.add(1, {
223
- ...strategyAttributes,
282
+ ...inclusionAttributes,
224
283
  [Attributes.OK]: false,
225
284
  ...strategyResult.exclusionReason && {
226
285
  [Attributes.ERROR_TYPE]: strategyResult.exclusionReason
@@ -231,15 +290,27 @@ export class SequencerMetrics {
231
290
  // Record strategy-specific priority fee delta
232
291
  if (strategyResult.priorityFeeDelta !== undefined) {
233
292
  const priorityFeeDeltaGwei = Number(strategyResult.priorityFeeDelta) / 1e9;
234
- this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, strategyAttributes);
293
+ const deltaAttributes = {
294
+ ...strategyAttributes,
295
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false'
296
+ };
297
+ this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, deltaAttributes);
235
298
  }
236
299
  // Record estimated cost if available
237
300
  if (strategyResult.estimatedCostEth !== undefined) {
238
- this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, strategyAttributes);
301
+ const costAttributes = {
302
+ ...strategyAttributes,
303
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false'
304
+ };
305
+ this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, costAttributes);
239
306
  }
240
307
  // Record estimated overpayment if available
241
308
  if (strategyResult.estimatedOverpaymentEth !== undefined) {
242
- this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, strategyAttributes);
309
+ const overpaymentAttributes = {
310
+ ...strategyAttributes,
311
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false'
312
+ };
313
+ this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, overpaymentAttributes);
243
314
  }
244
315
  }
245
316
  }
@@ -7,12 +7,12 @@ import type { DateProvider } from '@aztec/foundation/timer';
7
7
  import type { TypedEventEmitter } from '@aztec/foundation/types';
8
8
  import type { P2P } from '@aztec/p2p';
9
9
  import type { SlasherClientInterface } from '@aztec/slasher';
10
- import type { L2Block, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
10
+ import type { BlockData, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
11
11
  import type { Checkpoint } from '@aztec/stdlib/checkpoint';
12
12
  import { type ResolvedSequencerConfig, type SequencerConfig, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
13
13
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
14
14
  import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
15
- import { FullNodeCheckpointsBuilder, type ValidatorClient } from '@aztec/validator-client';
15
+ import { FullNodeCheckpointsBuilder, NodeKeystoreAdapter, type ValidatorClient } from '@aztec/validator-client';
16
16
  import type { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
17
17
  import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
18
18
  import type { InvalidateCheckpointRequest, SequencerPublisher } from '../publisher/sequencer-publisher.js';
@@ -52,6 +52,8 @@ export declare class Sequencer extends Sequencer_base {
52
52
  private metrics;
53
53
  /** The last slot for which we attempted to perform our voting duties with degraded block production */
54
54
  private lastSlotForFallbackVote;
55
+ /** The last slot for which we logged "no committee" warning, to avoid spam */
56
+ private lastSlotForNoCommitteeWarning;
55
57
  /** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */
56
58
  private lastSlotForCheckpointProposalJob;
57
59
  /** Last successful checkpoint proposed */
@@ -60,14 +62,13 @@ export declare class Sequencer extends Sequencer_base {
60
62
  private lastEpochForStrategyComparison;
61
63
  /** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */
62
64
  protected timetable: SequencerTimetable;
63
- protected publisher: SequencerPublisher | undefined;
64
65
  /** Config for the sequencer */
65
66
  protected config: ResolvedSequencerConfig;
66
67
  constructor(publisherFactory: SequencerPublisherFactory, validatorClient: ValidatorClient, globalsBuilder: GlobalVariableBuilder, p2pClient: P2P, worldState: WorldStateSynchronizer, slasherClient: SlasherClientInterface | undefined, l2BlockSource: L2BlockSource & L2BlockSink, l1ToL2MessageSource: L1ToL2MessageSource, checkpointsBuilder: FullNodeCheckpointsBuilder, l1Constants: SequencerRollupConstants, dateProvider: DateProvider, epochCache: EpochCache, rollupContract: RollupContract, config: SequencerConfig, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
67
68
  /** Updates sequencer config by the defined values and updates the timetable */
68
69
  updateConfig(config: Partial<SequencerConfig>): void;
69
- /** Initializes the sequencer (precomputes tables and creates a publisher). Takes about 3s. */
70
- init(): Promise<void>;
70
+ /** Initializes the sequencer (precomputes tables). Takes about 3s. */
71
+ init(): void;
71
72
  /** Starts the sequencer and moves to IDLE state. */
72
73
  start(): void;
73
74
  /** Stops the sequencer from building blocks and moves to STOPPED state. */
@@ -89,6 +90,10 @@ export declare class Sequencer extends Sequencer_base {
89
90
  protected work(): Promise<Checkpoint | undefined>;
90
91
  private prepareCheckpointProposal;
91
92
  protected createCheckpointProposalJob(epoch: EpochNumber, slot: SlotNumber, checkpointNumber: CheckpointNumber, syncedToBlockNumber: BlockNumber, proposer: EthAddress | undefined, publisher: SequencerPublisher, attestorAddress: EthAddress, invalidateCheckpoint: InvalidateCheckpointRequest | undefined): CheckpointProposalJob;
93
+ /**
94
+ * Returns the current sequencer state.
95
+ */
96
+ getState(): SequencerState;
92
97
  /**
93
98
  * Internal helper for setting the sequencer state and checks if we have enough time left in the slot to transition to the new state.
94
99
  * @param proposedState - The new state to transition to.
@@ -142,6 +147,8 @@ export declare class Sequencer extends Sequencer_base {
142
147
  getSlasherClient(): SlasherClientInterface | undefined;
143
148
  get tracer(): Tracer;
144
149
  getValidatorAddresses(): EthAddress[];
150
+ /** Updates the publisher factory's node keystore adapter after a keystore reload. */
151
+ updatePublisherNodeKeyStore(adapter: NodeKeystoreAdapter): void;
145
152
  getConfig(): {
146
153
  sequencerPollingIntervalMS: number;
147
154
  maxTxsPerBlock: number;
@@ -171,17 +178,20 @@ export declare class Sequencer extends Sequencer_base {
171
178
  fishermanMode: boolean;
172
179
  shuffleAttestationOrdering: boolean;
173
180
  blockDurationMs?: number | undefined;
181
+ expectedBlockProposalsPerSlot?: number | undefined;
174
182
  buildCheckpointIfEmpty: boolean;
175
183
  skipPushProposedBlocksToArchiver: boolean;
184
+ minBlocksForCheckpoint?: number | undefined;
185
+ skipPublishingCheckpointsPercent: number;
176
186
  };
177
187
  private get l1PublishingTime();
178
188
  }
179
189
  type SequencerSyncCheckResult = {
180
- block?: L2Block;
190
+ blockData?: BlockData;
181
191
  checkpointNumber: CheckpointNumber;
182
192
  blockNumber: BlockNumber;
183
193
  archive: Fr;
184
194
  l1Timestamp: bigint;
185
195
  pendingChainValidationStatus: ValidateCheckpointResult;
186
196
  };
187
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3NlcXVlbmNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQW9CLEtBQUssY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFekcsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUczRCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN0QyxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdELE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLHdCQUF3QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDekcsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFM0QsT0FBTyxFQUNMLEtBQUssdUJBQXVCLEVBQzVCLEtBQUssZUFBZSxFQUVwQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFHbkUsT0FBTyxFQUFjLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFpQyxNQUFNLHlCQUF5QixDQUFD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
197
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3NlcXVlbmNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQW9CLEtBQUssY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFekcsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUczRCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN0QyxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLHdCQUF3QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDM0csT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFM0QsT0FBTyxFQUNMLEtBQUssdUJBQXVCLEVBQzVCLEtBQUssZUFBZSxFQUVwQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFHbkUsT0FBTyxFQUFjLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFpQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZILE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxtQkFBbUIsRUFBRSxLQUFLLGVBQWUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBS2hILE9BQU8sS0FBSyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFDMUYsT0FBTyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQztBQUM3RixPQUFPLEtBQUssRUFBRSwyQkFBMkIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQzNHLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBR3JFLE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVuRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRCxPQUFPLEtBQUssRUFBRSx3QkFBd0IsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUMzRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLE9BQU8sRUFBRSxjQUFjLEVBQUUsQ0FBQzs7QUFFMUI7Ozs7Ozs7R0FPRztBQUNILHFCQUFhLFNBQVUsU0FBUSxjQUE4RDtJQTJCekYsU0FBUyxDQUFDLGdCQUFnQixFQUFFLHlCQUF5QjtJQUNyRCxTQUFTLENBQUMsZUFBZSxFQUFFLGVBQWU7SUFDMUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxxQkFBcUI7SUFDL0MsU0FBUyxDQUFDLFNBQVMsRUFBRSxHQUFHO0lBQ3hCLFNBQVMsQ0FBQyxVQUFVLEVBQUUsc0JBQXNCO0lBQzVDLFNBQVMsQ0FBQyxhQUFhLEVBQUUsc0JBQXNCLEdBQUcsU0FBUztJQUMzRCxTQUFTLENBQUMsYUFBYSxFQUFFLGFBQWEsR0FBRyxXQUFXO0lBQ3BELFNBQVMsQ0FBQyxtQkFBbUIsRUFBRSxtQkFBbUI7SUFDbEQsU0FBUyxDQUFDLGtCQUFrQixFQUFFLDBCQUEwQjtJQUN4RCxTQUFTLENBQUMsV0FBVyxFQUFFLHdCQUF3QjtJQUMvQyxTQUFTLENBQUMsWUFBWSxFQUFFLFlBQVk7SUFDcEMsU0FBUyxDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ2hDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsY0FBYztJQUV4QyxTQUFTLENBQUMsU0FBUyxFQUFFLGVBQWU7SUFDcEMsU0FBUyxDQUFDLEdBQUc7SUF6Q2YsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFpQjtJQUN4QyxPQUFPLENBQUMsS0FBSyxDQUEwQjtJQUN2QyxPQUFPLENBQUMsT0FBTyxDQUFtQjtJQUVsQyx1R0FBdUc7SUFDdkcsT0FBTyxDQUFDLHVCQUF1QixDQUF5QjtJQUV4RCw4RUFBOEU7SUFDOUUsT0FBTyxDQUFDLDZCQUE2QixDQUF5QjtJQUU5RCxxR0FBcUc7SUFDckcsT0FBTyxDQUFDLGdDQUFnQyxDQUF5QjtJQUVqRSwwQ0FBMEM7SUFDMUMsT0FBTyxDQUFDLHNCQUFzQixDQUF5QjtJQUV2RCxnRkFBZ0Y7SUFDaEYsT0FBTyxDQUFDLDhCQUE4QixDQUEwQjtJQUVoRSwrR0FBK0c7SUFDL0csU0FBUyxDQUFDLFNBQVMsRUFBRyxrQkFBa0IsQ0FBQztJQUV6QywrQkFBK0I7SUFDL0IsU0FBUyxDQUFDLE1BQU0sRUFBRSx1QkFBdUIsQ0FBMEI7SUFFbkUsWUFDWSxnQkFBZ0IsRUFBRSx5QkFBeUIsRUFDM0MsZUFBZSxFQUFFLGVBQWUsRUFDaEMsY0FBYyxFQUFFLHFCQUFxQixFQUNyQyxTQUFTLEVBQUUsR0FBRyxFQUNkLFVBQVUsRUFBRSxzQkFBc0IsRUFDbEMsYUFBYSxFQUFFLHNCQUFzQixHQUFHLFNBQVMsRUFDakQsYUFBYSxFQUFFLGFBQWEsR0FBRyxXQUFXLEVBQzFDLG1CQUFtQixFQUFFLG1CQUFtQixFQUN4QyxrQkFBa0IsRUFBRSwwQkFBMEIsRUFDOUMsV0FBVyxFQUFFLHdCQUF3QixFQUNyQyxZQUFZLEVBQUUsWUFBWSxFQUMxQixVQUFVLEVBQUUsVUFBVSxFQUN0QixjQUFjLEVBQUUsY0FBYyxFQUN4QyxNQUFNLEVBQUUsZUFBZSxFQUNiLFNBQVMsR0FBRSxlQUFzQyxFQUNqRCxHQUFHLHlDQUE0QixFQVcxQztJQUVELCtFQUErRTtJQUN4RSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxlQUFlLENBQUMsUUFnQm5EO0lBRUQsc0VBQXNFO0lBQy9ELElBQUksU0FFVjtJQUVELG9EQUFvRDtJQUM3QyxLQUFLLFNBU1g7SUFFRCwyRUFBMkU7SUFDOUQsSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FPakM7SUFFRCwyQ0FBMkM7SUFDM0MsVUFBZ0IsUUFBUSxrQkFvQnZCO0lBRUQsa0RBQWtEO0lBQzNDLE1BQU07O01BRVo7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsVUFDZ0IsSUFBSSxvQ0E0Qm5CO1lBUWEseUJBQXlCO0lBMkp2QyxTQUFTLENBQUMsMkJBQTJCLENBQ25DLEtBQUssRUFBRSxXQUFXLEVBQ2xCLElBQUksRUFBRSxVQUFVLEVBQ2hCLGdCQUFnQixFQUFFLGdCQUFnQixFQUNsQyxtQkFBbUIsRUFBRSxXQUFXLEVBQ2hDLFFBQVEsRUFBRSxVQUFVLEdBQUcsU0FBUyxFQUNoQyxTQUFTLEVBQUUsa0JBQWtCLEVBQzdCLGVBQWUsRUFBRSxVQUFVLEVBQzNCLG9CQUFvQixFQUFFLDJCQUEyQixHQUFHLFNBQVMsR0FDNUQscUJBQXFCLENBOEJ2QjtJQUVEOztPQUVHO0lBQ0ksUUFBUSxJQUFJLGNBQWMsQ0FFaEM7SUFFRDs7Ozs7T0FLRztJQUNILFNBQVMsQ0FBQyxRQUFRLENBQ2hCLGFBQWEsRUFBRSxjQUFjLEVBQzdCLFVBQVUsRUFBRSxVQUFVLEdBQUcsU0FBUyxFQUNsQyxJQUFJLEdBQUU7UUFBRSxLQUFLLENBQUMsRUFBRSxPQUFPLENBQUE7S0FBTyxHQUM3QixJQUFJLENBNkJOO0lBRUQ7OztPQUdHO0lBQ0gsVUFBZ0IsU0FBUyxDQUFDLElBQUksRUFBRTtRQUFFLEVBQUUsRUFBRSxNQUFNLENBQUM7UUFBQyxJQUFJLEVBQUUsVUFBVSxDQUFBO0tBQUUsR0FBRyxPQUFPLENBQUMsd0JBQXdCLEdBQUcsU0FBUyxDQUFDLENBc0UvRztJQUVEOzs7T0FHRztJQUNILFVBQWdCLGVBQWUsQ0FBQyxJQUFJLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxVQUFVLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FtQzVGO0lBRUQ7OztPQUdHO0lBQ0gsVUFDZ0Isb0JBQW9CLENBQUMsSUFBSSxFQUFFO1FBQUUsSUFBSSxFQUFFLFVBQVUsQ0FBQztRQUFDLEVBQUUsRUFBRSxNQUFNLENBQUE7S0FBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FvRTFGO0lBRUQ7OztPQUdHO0lBQ0gsVUFDZ0IsMEJBQTBCLENBQUMsSUFBSSxFQUFFO1FBQy9DLElBQUksRUFBRSxVQUFVLENBQUM7UUFDakIsUUFBUSxFQUFFLFVBQVUsR0FBRyxTQUFTLENBQUM7S0FDbEMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBc0NoQjtJQUVEOzs7OztPQUtHO0lBQ0gsVUFBZ0IsOEJBQThCLENBQzVDLFFBQVEsRUFBRSx3QkFBd0IsRUFDbEMsV0FBVyxFQUFFLFVBQVUsR0FDdEIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQW9GZjtJQUVELE9BQU8sQ0FBQyxxQkFBcUI7SUE2QjdCLE9BQU8sQ0FBQywwQkFBMEI7SUFJbEMsT0FBTyxDQUFDLGtCQUFrQjtJQUsxQixJQUFXLGlCQUFpQixXQUUzQjtJQUVELElBQVcsYUFBYSxJQUFJLE1BQU0sR0FBRyxTQUFTLENBRTdDO0lBRU0sZ0JBQWdCLElBQUksc0JBQXNCLEdBQUcsU0FBUyxDQUU1RDtJQUVELElBQVcsTUFBTSxJQUFJLE1BQU0sQ0FFMUI7SUFFTSxxQkFBcUIsaUJBRTNCO0lBRUQscUZBQXFGO0lBQzlFLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxtQkFBbUIsR0FBRyxJQUFJLENBRXJFO0lBRU0sU0FBUzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztNQUVmO0lBRUQsT0FBTyxLQUFLLGdCQUFnQixHQUUzQjtDQUNGO0FBRUQsS0FBSyx3QkFBd0IsR0FBRztJQUM5QixTQUFTLENBQUMsRUFBRSxTQUFTLENBQUM7SUFDdEIsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUM7SUFDbkMsV0FBVyxFQUFFLFdBQVcsQ0FBQztJQUN6QixPQUFPLEVBQUUsRUFBRSxDQUFDO0lBQ1osV0FBVyxFQUFFLE1BQU0sQ0FBQztJQUNwQiw0QkFBNEIsRUFBRSx3QkFBd0IsQ0FBQztDQUN4RCxDQUFDIn0=