@aztec/sequencer-client 0.0.1-commit.e6bd8901 → 0.0.1-commit.ec7ac5448

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 (93) hide show
  1. package/dest/client/sequencer-client.d.ts +17 -7
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +63 -30
  4. package/dest/config.d.ts +26 -7
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +47 -33
  7. package/dest/global_variable_builder/global_builder.d.ts +15 -10
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +26 -26
  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 +47 -17
  13. package/dest/publisher/config.d.ts.map +1 -1
  14. package/dest/publisher/config.js +121 -42
  15. package/dest/publisher/index.d.ts +2 -1
  16. package/dest/publisher/index.d.ts.map +1 -1
  17. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  18. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  19. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  20. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  21. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  22. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  23. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  24. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  25. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  26. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  27. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  28. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  29. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -5
  30. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  31. package/dest/publisher/sequencer-publisher-factory.js +27 -3
  32. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  33. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  34. package/dest/publisher/sequencer-publisher-metrics.js +12 -4
  35. package/dest/publisher/sequencer-publisher.d.ts +75 -49
  36. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  37. package/dest/publisher/sequencer-publisher.js +470 -156
  38. package/dest/sequencer/chain_state_overrides.d.ts +25 -0
  39. package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
  40. package/dest/sequencer/chain_state_overrides.js +39 -0
  41. package/dest/sequencer/checkpoint_proposal_job.d.ts +51 -13
  42. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  43. package/dest/sequencer/checkpoint_proposal_job.js +414 -239
  44. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  45. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  46. package/dest/sequencer/checkpoint_voter.js +2 -5
  47. package/dest/sequencer/events.d.ts +2 -1
  48. package/dest/sequencer/events.d.ts.map +1 -1
  49. package/dest/sequencer/metrics.d.ts +25 -6
  50. package/dest/sequencer/metrics.d.ts.map +1 -1
  51. package/dest/sequencer/metrics.js +134 -31
  52. package/dest/sequencer/sequencer.d.ts +44 -17
  53. package/dest/sequencer/sequencer.d.ts.map +1 -1
  54. package/dest/sequencer/sequencer.js +157 -94
  55. package/dest/sequencer/timetable.d.ts +17 -6
  56. package/dest/sequencer/timetable.d.ts.map +1 -1
  57. package/dest/sequencer/timetable.js +51 -46
  58. package/dest/sequencer/types.d.ts +2 -2
  59. package/dest/sequencer/types.d.ts.map +1 -1
  60. package/dest/test/index.d.ts +3 -5
  61. package/dest/test/index.d.ts.map +1 -1
  62. package/dest/test/mock_checkpoint_builder.d.ts +14 -11
  63. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  64. package/dest/test/mock_checkpoint_builder.js +63 -40
  65. package/dest/test/utils.d.ts +3 -3
  66. package/dest/test/utils.d.ts.map +1 -1
  67. package/dest/test/utils.js +5 -4
  68. package/package.json +27 -28
  69. package/src/client/sequencer-client.ts +81 -30
  70. package/src/config.ts +65 -43
  71. package/src/global_variable_builder/global_builder.ts +31 -28
  72. package/src/global_variable_builder/index.ts +1 -1
  73. package/src/publisher/config.ts +153 -43
  74. package/src/publisher/index.ts +3 -0
  75. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  76. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  77. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  78. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  79. package/src/publisher/sequencer-publisher-factory.ts +38 -9
  80. package/src/publisher/sequencer-publisher-metrics.ts +7 -3
  81. package/src/publisher/sequencer-publisher.ts +531 -214
  82. package/src/sequencer/README.md +83 -13
  83. package/src/sequencer/chain_state_overrides.ts +87 -0
  84. package/src/sequencer/checkpoint_proposal_job.ts +547 -274
  85. package/src/sequencer/checkpoint_voter.ts +1 -12
  86. package/src/sequencer/events.ts +1 -1
  87. package/src/sequencer/metrics.ts +153 -33
  88. package/src/sequencer/sequencer.ts +228 -107
  89. package/src/sequencer/timetable.ts +70 -57
  90. package/src/sequencer/types.ts +1 -1
  91. package/src/test/index.ts +2 -4
  92. package/src/test/mock_checkpoint_builder.ts +84 -56
  93. package/src/test/utils.ts +5 -2
@@ -2,7 +2,6 @@ import type { SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import type { Logger } from '@aztec/foundation/log';
4
4
  import type { SlasherClientInterface } from '@aztec/slasher';
5
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
6
5
  import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
7
6
  import type { ValidatorClient } from '@aztec/validator-client';
8
7
  import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
@@ -18,7 +17,6 @@ import type { SequencerRollupConstants } from './types.js';
18
17
  * Handles governance and slashing voting for a given slot.
19
18
  */
20
19
  export class CheckpointVoter {
21
- private slotTimestamp: bigint;
22
20
  private governanceSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
23
21
  private slashingSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
24
22
 
@@ -33,8 +31,6 @@ export class CheckpointVoter {
33
31
  private readonly metrics: SequencerMetrics,
34
32
  private readonly log: Logger,
35
33
  ) {
36
- this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
37
-
38
34
  // Create separate signers with appropriate duty contexts for governance and slashing votes
39
35
  // These use HA protection to ensure only one node signs per slot/duty
40
36
  const governanceContext: SigningContext = { slot: this.slot, dutyType: DutyType.GOVERNANCE_VOTE };
@@ -77,7 +73,6 @@ export class CheckpointVoter {
77
73
  return await this.publisher.enqueueGovernanceCastSignal(
78
74
  governanceProposerPayload,
79
75
  this.slot,
80
- this.slotTimestamp,
81
76
  this.attestorAddress,
82
77
  this.governanceSigner,
83
78
  );
@@ -108,13 +103,7 @@ export class CheckpointVoter {
108
103
 
109
104
  this.metrics.recordSlashingAttempt(actions.length);
110
105
 
111
- return await this.publisher.enqueueSlashingActions(
112
- actions,
113
- this.slot,
114
- this.slotTimestamp,
115
- this.attestorAddress,
116
- this.slashingSigner,
117
- );
106
+ return await this.publisher.enqueueSlashingActions(actions, this.slot, this.attestorAddress, this.slashingSigner);
118
107
  } catch (err) {
119
108
  if (err instanceof DutyAlreadySignedError) {
120
109
  this.log.info(`Slashing vote already signed by another node`, {
@@ -13,7 +13,7 @@ export type SequencerEvents = {
13
13
  ['proposer-rollup-check-failed']: (args: { reason: string; slot: SlotNumber }) => void;
14
14
  ['block-tx-count-check-failed']: (args: { minTxs: number; availableTxs: number; slot: SlotNumber }) => void;
15
15
  ['block-build-failed']: (args: { reason: string; slot: SlotNumber }) => void;
16
- ['block-proposed']: (args: { blockNumber: BlockNumber; slot: SlotNumber }) => void;
16
+ ['block-proposed']: (args: { blockNumber: BlockNumber; slot: SlotNumber; buildSlot: SlotNumber }) => void;
17
17
  ['checkpoint-empty']: (args: { slot: SlotNumber }) => void;
18
18
  ['checkpoint-publish-failed']: (args: {
19
19
  slot: SlotNumber;
@@ -11,13 +11,13 @@ import {
11
11
  type TelemetryClient,
12
12
  type Tracer,
13
13
  type UpDownCounter,
14
+ createUpDownCounterWithDefault,
14
15
  } from '@aztec/telemetry-client';
15
16
 
16
17
  import { type Hex, formatUnits } from 'viem';
17
18
 
18
19
  import type { SequencerState } from './utils.js';
19
20
 
20
- // TODO(palla/mbps): Review all metrics and add any missing ones per checkpoint
21
21
  export class SequencerMetrics {
22
22
  public readonly tracer: Tracer;
23
23
  private meter: Meter;
@@ -39,17 +39,28 @@ export class SequencerMetrics {
39
39
  private filledSlots: UpDownCounter;
40
40
 
41
41
  private blockProposalFailed: UpDownCounter;
42
- private blockProposalSuccess: UpDownCounter;
43
- private blockProposalPrecheckFailed: UpDownCounter;
42
+ private checkpointProposalSuccess: UpDownCounter;
43
+ private checkpointPrecheckFailed: UpDownCounter;
44
+ private checkpointProposalFailed: UpDownCounter;
44
45
  private checkpointSuccess: UpDownCounter;
45
46
  private slashingAttempts: UpDownCounter;
46
47
  private checkpointAttestationDelay: Histogram;
48
+ private checkpointBuildDuration: Histogram;
49
+ private checkpointBlockCount: Gauge;
50
+ private checkpointTxCount: Gauge;
51
+ private checkpointTotalMana: Gauge;
52
+ private pipelineDepth: Gauge;
53
+ private pipelineDiscards: UpDownCounter;
47
54
 
48
55
  // Fisherman fee analysis metrics
49
56
  private fishermanWouldBeIncluded: UpDownCounter;
50
57
  private fishermanTimeBeforeBlock: Histogram;
51
58
  private fishermanPendingBlobTxCount: Histogram;
52
59
  private fishermanIncludedBlobTxCount: Histogram;
60
+ private fishermanPendingBlobCount: Histogram;
61
+ private fishermanIncludedBlobCount: Histogram;
62
+ private fishermanBlockBlobsFull: UpDownCounter;
63
+ private fishermanMaxBlobCapacity: Histogram;
53
64
  private fishermanCalculatedPriorityFee: Histogram;
54
65
  private fishermanPriorityFeeDelta: Histogram;
55
66
  private fishermanEstimatedCost: Histogram;
@@ -57,6 +68,10 @@ export class SequencerMetrics {
57
68
  private fishermanMinedBlobTxPriorityFee: Histogram;
58
69
  private fishermanMinedBlobTxTotalCost: Histogram;
59
70
 
71
+ private blockInterBlockTime: Histogram;
72
+ private lastBlockBuiltTimestamp?: number;
73
+ private lastBlockBuiltSlot?: SlotNumber;
74
+
60
75
  private lastSeenSlot?: SlotNumber;
61
76
 
62
77
  constructor(
@@ -67,33 +82,29 @@ export class SequencerMetrics {
67
82
  this.meter = client.getMeter(name);
68
83
  this.tracer = client.getTracer(name);
69
84
 
70
- this.blockCounter = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
85
+ this.blockCounter = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_COUNT, {
86
+ [Attributes.STATUS]: ['failed', 'built'],
87
+ });
71
88
 
72
89
  this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
73
90
 
74
91
  this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
75
92
 
93
+ this.blockInterBlockTime = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_INTER_BLOCK_TIME);
94
+
76
95
  this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
77
96
 
78
97
  this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
79
98
 
80
- // Init gauges and counters
81
- this.blockCounter.add(0, {
82
- [Attributes.STATUS]: 'failed',
83
- });
84
- this.blockCounter.add(0, {
85
- [Attributes.STATUS]: 'built',
86
- });
99
+ this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
87
100
 
88
- this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS);
89
-
90
- this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT);
101
+ this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
91
102
 
92
103
  /**
93
104
  * NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
94
105
  * Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
95
106
  */
96
- this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT);
107
+ this.filledSlots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_FILLED_SLOT_COUNT);
97
108
 
98
109
  this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
99
110
 
@@ -103,20 +114,56 @@ export class SequencerMetrics {
103
114
 
104
115
  this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
105
116
 
106
- this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT);
117
+ this.blockProposalFailed = createUpDownCounterWithDefault(
118
+ this.meter,
119
+ Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT,
120
+ );
107
121
 
108
- this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT);
122
+ this.checkpointProposalSuccess = createUpDownCounterWithDefault(
123
+ this.meter,
124
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_SUCCESS_COUNT,
125
+ );
109
126
 
110
- this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
127
+ this.checkpointSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
128
+
129
+ this.checkpointPrecheckFailed = createUpDownCounterWithDefault(
130
+ this.meter,
131
+ Metrics.SEQUENCER_CHECKPOINT_PRECHECK_FAILED_COUNT,
132
+ {
133
+ [Attributes.ERROR_TYPE]: [
134
+ 'slot_already_taken',
135
+ 'rollup_contract_check_failed',
136
+ 'slot_mismatch',
137
+ 'block_number_mismatch',
138
+ ],
139
+ },
140
+ );
111
141
 
112
- this.blockProposalPrecheckFailed = this.meter.createUpDownCounter(
113
- Metrics.SEQUENCER_BLOCK_PROPOSAL_PRECHECK_FAILED_COUNT,
142
+ this.checkpointProposalFailed = createUpDownCounterWithDefault(
143
+ this.meter,
144
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
114
145
  );
115
146
 
116
- this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
147
+ this.checkpointBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION);
148
+ this.checkpointBlockCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT);
149
+ this.checkpointTxCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT);
150
+ this.checkpointTotalMana = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA);
151
+
152
+ this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
153
+
154
+ this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
155
+ this.pipelineDiscards = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_PIPELINE_DISCARDS_COUNT);
156
+ this.pipelineDepth.record(0);
117
157
 
118
158
  // Fisherman fee analysis metrics
119
- this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED);
159
+ this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
160
+ this.meter,
161
+ Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED,
162
+ {
163
+ [Attributes.OK]: [true, false],
164
+ [Attributes.BLOCK_FULL]: ['true', 'false'],
165
+ },
166
+ );
120
167
 
121
168
  this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
122
169
 
@@ -145,6 +192,20 @@ export class SequencerMetrics {
145
192
  this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
146
193
  Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
147
194
  );
195
+
196
+ this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
197
+
198
+ this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
199
+
200
+ this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(
201
+ this.meter,
202
+ Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL,
203
+ {
204
+ [Attributes.OK]: [true, false],
205
+ },
206
+ );
207
+
208
+ this.fishermanMaxBlobCapacity = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MAX_BLOB_CAPACITY);
148
209
  }
149
210
 
150
211
  public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
@@ -165,12 +226,20 @@ export class SequencerMetrics {
165
226
  this.timeToCollectAttestations.record(Math.ceil(durationMs));
166
227
  }
167
228
 
168
- recordBuiltBlock(buildDurationMs: number, totalMana: number) {
229
+ recordBuiltBlock(buildDurationMs: number, totalMana: number, slot: SlotNumber) {
169
230
  this.blockCounter.add(1, {
170
231
  [Attributes.STATUS]: 'built',
171
232
  });
172
233
  this.blockBuildDuration.record(Math.ceil(buildDurationMs));
173
234
  this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
235
+
236
+ // Only record inter-block time between blocks built within the same slot.
237
+ const now = Date.now();
238
+ if (this.lastBlockBuiltTimestamp !== undefined && this.lastBlockBuiltSlot === slot) {
239
+ this.blockInterBlockTime.record(now - this.lastBlockBuiltTimestamp);
240
+ }
241
+ this.lastBlockBuiltTimestamp = now;
242
+ this.lastBlockBuiltSlot = slot;
174
243
  }
175
244
 
176
245
  recordFailedBlock() {
@@ -185,6 +254,14 @@ export class SequencerMetrics {
185
254
  });
186
255
  }
187
256
 
257
+ recordPipelineDepth(depth: number) {
258
+ this.pipelineDepth.record(depth);
259
+ }
260
+
261
+ recordPipelineDiscard(count = 1) {
262
+ this.pipelineDiscards.add(count);
263
+ }
264
+
188
265
  incOpenSlot(slot: SlotNumber, proposer: string) {
189
266
  // sequencer went through the loop a second time. Noop
190
267
  if (slot === this.lastSeenSlot) {
@@ -227,16 +304,30 @@ export class SequencerMetrics {
227
304
  });
228
305
  }
229
306
 
230
- recordBlockProposalSuccess() {
231
- this.blockProposalSuccess.add(1);
307
+ recordCheckpointProposalSuccess() {
308
+ this.checkpointProposalSuccess.add(1);
232
309
  }
233
310
 
234
- recordBlockProposalPrecheckFailed(checkType: string) {
235
- this.blockProposalPrecheckFailed.add(1, {
236
- [Attributes.ERROR_TYPE]: checkType,
311
+ recordCheckpointPrecheckFailed(
312
+ checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch',
313
+ ) {
314
+ this.checkpointPrecheckFailed.add(1, { [Attributes.ERROR_TYPE]: checkType });
315
+ }
316
+
317
+ recordCheckpointProposalFailed(reason?: string) {
318
+ this.checkpointProposalFailed.add(1, {
319
+ ...(reason && { [Attributes.ERROR_TYPE]: reason }),
237
320
  });
238
321
  }
239
322
 
323
+ /** Records aggregate metrics for a completed checkpoint build. */
324
+ recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
325
+ this.checkpointBuildDuration.record(Math.ceil(durationMs));
326
+ this.checkpointBlockCount.record(blockCount);
327
+ this.checkpointTxCount.record(txCount);
328
+ this.checkpointTotalMana.record(totalMana);
329
+ }
330
+
240
331
  recordSlashingAttempt(actionCount: number) {
241
332
  this.slashingAttempts.add(actionCount);
242
333
  }
@@ -263,10 +354,12 @@ export class SequencerMetrics {
263
354
 
264
355
  // Record pending block snapshot data (once per strategy for comparison)
265
356
  this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
357
+ this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
266
358
 
267
359
  // Record mined block data if available
268
360
  if (analysis.minedBlock) {
269
361
  this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
362
+ this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
270
363
 
271
364
  // Record actual fees from blob transactions in the mined block
272
365
  for (const blobTx of analysis.minedBlock.includedBlobTxs) {
@@ -300,13 +393,28 @@ export class SequencerMetrics {
300
393
  if (analysis.analysis) {
301
394
  this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
302
395
 
396
+ // Record whether the block reached 100% blob capacity
397
+ if (analysis.analysis.blockBlobsFull) {
398
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: true });
399
+ } else {
400
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: false });
401
+ }
402
+
403
+ // Record the max blob capacity for this block
404
+ this.fishermanMaxBlobCapacity.record(analysis.analysis.maxBlobCapacity, strategyAttributes);
405
+
303
406
  // Record strategy-specific inclusion result
304
407
  if (strategyResult.wouldBeIncluded !== undefined) {
408
+ const inclusionAttributes = {
409
+ ...strategyAttributes,
410
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
411
+ };
412
+
305
413
  if (strategyResult.wouldBeIncluded) {
306
- this.fishermanWouldBeIncluded.add(1, { ...strategyAttributes, [Attributes.OK]: true });
414
+ this.fishermanWouldBeIncluded.add(1, { ...inclusionAttributes, [Attributes.OK]: true });
307
415
  } else {
308
416
  this.fishermanWouldBeIncluded.add(1, {
309
- ...strategyAttributes,
417
+ ...inclusionAttributes,
310
418
  [Attributes.OK]: false,
311
419
  ...(strategyResult.exclusionReason && { [Attributes.ERROR_TYPE]: strategyResult.exclusionReason }),
312
420
  });
@@ -316,17 +424,29 @@ export class SequencerMetrics {
316
424
  // Record strategy-specific priority fee delta
317
425
  if (strategyResult.priorityFeeDelta !== undefined) {
318
426
  const priorityFeeDeltaGwei = Number(strategyResult.priorityFeeDelta) / 1e9;
319
- this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, strategyAttributes);
427
+ const deltaAttributes = {
428
+ ...strategyAttributes,
429
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
430
+ };
431
+ this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, deltaAttributes);
320
432
  }
321
433
 
322
434
  // Record estimated cost if available
323
435
  if (strategyResult.estimatedCostEth !== undefined) {
324
- this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, strategyAttributes);
436
+ const costAttributes = {
437
+ ...strategyAttributes,
438
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
439
+ };
440
+ this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, costAttributes);
325
441
  }
326
442
 
327
443
  // Record estimated overpayment if available
328
444
  if (strategyResult.estimatedOverpaymentEth !== undefined) {
329
- this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, strategyAttributes);
445
+ const overpaymentAttributes = {
446
+ ...strategyAttributes,
447
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
448
+ };
449
+ this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, overpaymentAttributes);
330
450
  }
331
451
  }
332
452
  }