@aztec/sequencer-client 0.0.1-commit.6d3c34e → 0.0.1-commit.7035c9bd6

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 (97) 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 +56 -17
  4. package/dest/config.d.ts +26 -7
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +47 -30
  7. package/dest/global_variable_builder/global_builder.d.ts +2 -4
  8. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  9. package/dest/global_variable_builder/global_builder.js +7 -6
  10. package/dest/index.d.ts +2 -2
  11. package/dest/index.d.ts.map +1 -1
  12. package/dest/index.js +1 -1
  13. package/dest/publisher/config.d.ts +35 -17
  14. package/dest/publisher/config.d.ts.map +1 -1
  15. package/dest/publisher/config.js +106 -42
  16. package/dest/publisher/index.d.ts +2 -1
  17. package/dest/publisher/index.d.ts.map +1 -1
  18. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  19. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  20. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  21. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  22. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  23. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  24. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  25. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  26. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  27. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  28. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  29. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  30. package/dest/publisher/sequencer-publisher-factory.d.ts +11 -3
  31. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  32. package/dest/publisher/sequencer-publisher-factory.js +27 -2
  33. package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
  34. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  35. package/dest/publisher/sequencer-publisher-metrics.js +12 -4
  36. package/dest/publisher/sequencer-publisher.d.ts +30 -10
  37. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  38. package/dest/publisher/sequencer-publisher.js +362 -56
  39. package/dest/sequencer/checkpoint_proposal_job.d.ts +42 -11
  40. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  41. package/dest/sequencer/checkpoint_proposal_job.js +322 -122
  42. package/dest/sequencer/checkpoint_voter.d.ts +3 -2
  43. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  44. package/dest/sequencer/checkpoint_voter.js +34 -10
  45. package/dest/sequencer/events.d.ts +2 -1
  46. package/dest/sequencer/events.d.ts.map +1 -1
  47. package/dest/sequencer/index.d.ts +1 -2
  48. package/dest/sequencer/index.d.ts.map +1 -1
  49. package/dest/sequencer/index.js +0 -1
  50. package/dest/sequencer/metrics.d.ts +21 -5
  51. package/dest/sequencer/metrics.d.ts.map +1 -1
  52. package/dest/sequencer/metrics.js +122 -30
  53. package/dest/sequencer/sequencer.d.ts +43 -20
  54. package/dest/sequencer/sequencer.d.ts.map +1 -1
  55. package/dest/sequencer/sequencer.js +151 -82
  56. package/dest/sequencer/timetable.d.ts +4 -6
  57. package/dest/sequencer/timetable.d.ts.map +1 -1
  58. package/dest/sequencer/timetable.js +7 -11
  59. package/dest/sequencer/types.d.ts +2 -2
  60. package/dest/sequencer/types.d.ts.map +1 -1
  61. package/dest/test/index.d.ts +3 -5
  62. package/dest/test/index.d.ts.map +1 -1
  63. package/dest/test/mock_checkpoint_builder.d.ts +23 -19
  64. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  65. package/dest/test/mock_checkpoint_builder.js +67 -38
  66. package/dest/test/utils.d.ts +8 -8
  67. package/dest/test/utils.d.ts.map +1 -1
  68. package/dest/test/utils.js +12 -11
  69. package/package.json +29 -28
  70. package/src/client/sequencer-client.ts +77 -18
  71. package/src/config.ts +66 -41
  72. package/src/global_variable_builder/global_builder.ts +6 -5
  73. package/src/index.ts +1 -6
  74. package/src/publisher/config.ts +121 -43
  75. package/src/publisher/index.ts +3 -0
  76. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  77. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  78. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  79. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  80. package/src/publisher/sequencer-publisher-factory.ts +38 -6
  81. package/src/publisher/sequencer-publisher-metrics.ts +7 -3
  82. package/src/publisher/sequencer-publisher.ts +360 -69
  83. package/src/sequencer/checkpoint_proposal_job.ts +449 -142
  84. package/src/sequencer/checkpoint_voter.ts +32 -7
  85. package/src/sequencer/events.ts +1 -1
  86. package/src/sequencer/index.ts +0 -1
  87. package/src/sequencer/metrics.ts +138 -32
  88. package/src/sequencer/sequencer.ts +200 -91
  89. package/src/sequencer/timetable.ts +13 -12
  90. package/src/sequencer/types.ts +1 -1
  91. package/src/test/index.ts +2 -4
  92. package/src/test/mock_checkpoint_builder.ts +122 -78
  93. package/src/test/utils.ts +24 -14
  94. package/dest/sequencer/block_builder.d.ts +0 -26
  95. package/dest/sequencer/block_builder.d.ts.map +0 -1
  96. package/dest/sequencer/block_builder.js +0 -129
  97. package/src/sequencer/block_builder.ts +0 -216
@@ -5,6 +5,8 @@ import type { SlasherClientInterface } from '@aztec/slasher';
5
5
  import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
6
6
  import type { ResolvedSequencerConfig } from '@aztec/stdlib/interfaces/server';
7
7
  import type { ValidatorClient } from '@aztec/validator-client';
8
+ import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
9
+ import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
8
10
 
9
11
  import type { TypedDataDefinition } from 'viem';
10
12
 
@@ -17,7 +19,8 @@ import type { SequencerRollupConstants } from './types.js';
17
19
  */
18
20
  export class CheckpointVoter {
19
21
  private slotTimestamp: bigint;
20
- private signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
22
+ private governanceSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
23
+ private slashingSigner: (msg: TypedDataDefinition) => Promise<`0x${string}`>;
21
24
 
22
25
  constructor(
23
26
  private readonly slot: SlotNumber,
@@ -31,8 +34,16 @@ export class CheckpointVoter {
31
34
  private readonly log: Logger,
32
35
  ) {
33
36
  this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
34
- this.signer = (msg: TypedDataDefinition) =>
35
- this.validatorClient.signWithAddress(this.attestorAddress, msg).then(s => s.toString());
37
+
38
+ // Create separate signers with appropriate duty contexts for governance and slashing votes
39
+ // These use HA protection to ensure only one node signs per slot/duty
40
+ const governanceContext: SigningContext = { slot: this.slot, dutyType: DutyType.GOVERNANCE_VOTE };
41
+ this.governanceSigner = (msg: TypedDataDefinition) =>
42
+ this.validatorClient.signWithAddress(this.attestorAddress, msg, governanceContext).then(s => s.toString());
43
+
44
+ const slashingContext: SigningContext = { slot: this.slot, dutyType: DutyType.SLASHING_VOTE };
45
+ this.slashingSigner = (msg: TypedDataDefinition) =>
46
+ this.validatorClient.signWithAddress(this.attestorAddress, msg, slashingContext).then(s => s.toString());
36
47
  }
37
48
 
38
49
  /**
@@ -68,10 +79,17 @@ export class CheckpointVoter {
68
79
  this.slot,
69
80
  this.slotTimestamp,
70
81
  this.attestorAddress,
71
- this.signer,
82
+ this.governanceSigner,
72
83
  );
73
84
  } catch (err) {
74
- this.log.error(`Error enqueuing governance vote`, err, { slot: this.slot });
85
+ if (err instanceof DutyAlreadySignedError) {
86
+ this.log.info(`Governance vote already signed by another node`, {
87
+ slot: this.slot,
88
+ signedByNode: err.signedByNode,
89
+ });
90
+ } else {
91
+ this.log.error(`Error enqueueing governance vote`, err);
92
+ }
75
93
  return false;
76
94
  }
77
95
  }
@@ -95,10 +113,17 @@ export class CheckpointVoter {
95
113
  this.slot,
96
114
  this.slotTimestamp,
97
115
  this.attestorAddress,
98
- this.signer,
116
+ this.slashingSigner,
99
117
  );
100
118
  } catch (err) {
101
- this.log.error(`Error enqueuing slashing vote`, err, { slot: this.slot });
119
+ if (err instanceof DutyAlreadySignedError) {
120
+ this.log.info(`Slashing vote already signed by another node`, {
121
+ slot: this.slot,
122
+ signedByNode: err.signedByNode,
123
+ });
124
+ } else {
125
+ this.log.error(`Error enqueueing slashing vote`, err);
126
+ }
102
127
  return false;
103
128
  }
104
129
  }
@@ -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;
@@ -1,4 +1,3 @@
1
- export * from './block_builder.js';
2
1
  export * from './checkpoint_proposal_job.js';
3
2
  export * from './checkpoint_voter.js';
4
3
  export * from './config.js';
@@ -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;
@@ -67,7 +78,9 @@ export class SequencerMetrics {
67
78
  this.meter = client.getMeter(name);
68
79
  this.tracer = client.getTracer(name);
69
80
 
70
- this.blockCounter = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
81
+ this.blockCounter = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_COUNT, {
82
+ [Attributes.STATUS]: ['failed', 'built'],
83
+ });
71
84
 
72
85
  this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
73
86
 
@@ -77,23 +90,15 @@ export class SequencerMetrics {
77
90
 
78
91
  this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
79
92
 
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
- });
87
-
88
- this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS);
93
+ this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
89
94
 
90
- this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT);
95
+ this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
91
96
 
92
97
  /**
93
98
  * NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
94
99
  * Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
95
100
  */
96
- this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT);
101
+ this.filledSlots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_FILLED_SLOT_COUNT);
97
102
 
98
103
  this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
99
104
 
@@ -103,20 +108,56 @@ export class SequencerMetrics {
103
108
 
104
109
  this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
105
110
 
106
- this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT);
111
+ this.blockProposalFailed = createUpDownCounterWithDefault(
112
+ this.meter,
113
+ Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT,
114
+ );
107
115
 
108
- this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT);
116
+ this.checkpointProposalSuccess = createUpDownCounterWithDefault(
117
+ this.meter,
118
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_SUCCESS_COUNT,
119
+ );
109
120
 
110
- this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
121
+ this.checkpointSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
122
+
123
+ this.checkpointPrecheckFailed = createUpDownCounterWithDefault(
124
+ this.meter,
125
+ Metrics.SEQUENCER_CHECKPOINT_PRECHECK_FAILED_COUNT,
126
+ {
127
+ [Attributes.ERROR_TYPE]: [
128
+ 'slot_already_taken',
129
+ 'rollup_contract_check_failed',
130
+ 'slot_mismatch',
131
+ 'block_number_mismatch',
132
+ ],
133
+ },
134
+ );
111
135
 
112
- this.blockProposalPrecheckFailed = this.meter.createUpDownCounter(
113
- Metrics.SEQUENCER_BLOCK_PROPOSAL_PRECHECK_FAILED_COUNT,
136
+ this.checkpointProposalFailed = createUpDownCounterWithDefault(
137
+ this.meter,
138
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
114
139
  );
115
140
 
116
- this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
141
+ this.checkpointBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION);
142
+ this.checkpointBlockCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT);
143
+ this.checkpointTxCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT);
144
+ this.checkpointTotalMana = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA);
145
+
146
+ this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
147
+
148
+ this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
149
+ this.pipelineDiscards = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_PIPELINE_DISCARDS_COUNT);
150
+ this.pipelineDepth.record(0);
117
151
 
118
152
  // Fisherman fee analysis metrics
119
- this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED);
153
+ this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
154
+ this.meter,
155
+ Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED,
156
+ {
157
+ [Attributes.OK]: [true, false],
158
+ [Attributes.BLOCK_FULL]: ['true', 'false'],
159
+ },
160
+ );
120
161
 
121
162
  this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
122
163
 
@@ -145,6 +186,20 @@ export class SequencerMetrics {
145
186
  this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
146
187
  Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
147
188
  );
189
+
190
+ this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
191
+
192
+ this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
193
+
194
+ this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(
195
+ this.meter,
196
+ Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL,
197
+ {
198
+ [Attributes.OK]: [true, false],
199
+ },
200
+ );
201
+
202
+ this.fishermanMaxBlobCapacity = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MAX_BLOB_CAPACITY);
148
203
  }
149
204
 
150
205
  public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
@@ -185,6 +240,14 @@ export class SequencerMetrics {
185
240
  });
186
241
  }
187
242
 
243
+ recordPipelineDepth(depth: number) {
244
+ this.pipelineDepth.record(depth);
245
+ }
246
+
247
+ recordPipelineDiscard(count = 1) {
248
+ this.pipelineDiscards.add(count);
249
+ }
250
+
188
251
  incOpenSlot(slot: SlotNumber, proposer: string) {
189
252
  // sequencer went through the loop a second time. Noop
190
253
  if (slot === this.lastSeenSlot) {
@@ -227,16 +290,30 @@ export class SequencerMetrics {
227
290
  });
228
291
  }
229
292
 
230
- recordBlockProposalSuccess() {
231
- this.blockProposalSuccess.add(1);
293
+ recordCheckpointProposalSuccess() {
294
+ this.checkpointProposalSuccess.add(1);
295
+ }
296
+
297
+ recordCheckpointPrecheckFailed(
298
+ checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch',
299
+ ) {
300
+ this.checkpointPrecheckFailed.add(1, { [Attributes.ERROR_TYPE]: checkType });
232
301
  }
233
302
 
234
- recordBlockProposalPrecheckFailed(checkType: string) {
235
- this.blockProposalPrecheckFailed.add(1, {
236
- [Attributes.ERROR_TYPE]: checkType,
303
+ recordCheckpointProposalFailed(reason?: string) {
304
+ this.checkpointProposalFailed.add(1, {
305
+ ...(reason && { [Attributes.ERROR_TYPE]: reason }),
237
306
  });
238
307
  }
239
308
 
309
+ /** Records aggregate metrics for a completed checkpoint build. */
310
+ recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
311
+ this.checkpointBuildDuration.record(Math.ceil(durationMs));
312
+ this.checkpointBlockCount.record(blockCount);
313
+ this.checkpointTxCount.record(txCount);
314
+ this.checkpointTotalMana.record(totalMana);
315
+ }
316
+
240
317
  recordSlashingAttempt(actionCount: number) {
241
318
  this.slashingAttempts.add(actionCount);
242
319
  }
@@ -263,10 +340,12 @@ export class SequencerMetrics {
263
340
 
264
341
  // Record pending block snapshot data (once per strategy for comparison)
265
342
  this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
343
+ this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
266
344
 
267
345
  // Record mined block data if available
268
346
  if (analysis.minedBlock) {
269
347
  this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
348
+ this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
270
349
 
271
350
  // Record actual fees from blob transactions in the mined block
272
351
  for (const blobTx of analysis.minedBlock.includedBlobTxs) {
@@ -300,13 +379,28 @@ export class SequencerMetrics {
300
379
  if (analysis.analysis) {
301
380
  this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
302
381
 
382
+ // Record whether the block reached 100% blob capacity
383
+ if (analysis.analysis.blockBlobsFull) {
384
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: true });
385
+ } else {
386
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: false });
387
+ }
388
+
389
+ // Record the max blob capacity for this block
390
+ this.fishermanMaxBlobCapacity.record(analysis.analysis.maxBlobCapacity, strategyAttributes);
391
+
303
392
  // Record strategy-specific inclusion result
304
393
  if (strategyResult.wouldBeIncluded !== undefined) {
394
+ const inclusionAttributes = {
395
+ ...strategyAttributes,
396
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
397
+ };
398
+
305
399
  if (strategyResult.wouldBeIncluded) {
306
- this.fishermanWouldBeIncluded.add(1, { ...strategyAttributes, [Attributes.OK]: true });
400
+ this.fishermanWouldBeIncluded.add(1, { ...inclusionAttributes, [Attributes.OK]: true });
307
401
  } else {
308
402
  this.fishermanWouldBeIncluded.add(1, {
309
- ...strategyAttributes,
403
+ ...inclusionAttributes,
310
404
  [Attributes.OK]: false,
311
405
  ...(strategyResult.exclusionReason && { [Attributes.ERROR_TYPE]: strategyResult.exclusionReason }),
312
406
  });
@@ -316,17 +410,29 @@ export class SequencerMetrics {
316
410
  // Record strategy-specific priority fee delta
317
411
  if (strategyResult.priorityFeeDelta !== undefined) {
318
412
  const priorityFeeDeltaGwei = Number(strategyResult.priorityFeeDelta) / 1e9;
319
- this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, strategyAttributes);
413
+ const deltaAttributes = {
414
+ ...strategyAttributes,
415
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
416
+ };
417
+ this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, deltaAttributes);
320
418
  }
321
419
 
322
420
  // Record estimated cost if available
323
421
  if (strategyResult.estimatedCostEth !== undefined) {
324
- this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, strategyAttributes);
422
+ const costAttributes = {
423
+ ...strategyAttributes,
424
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
425
+ };
426
+ this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, costAttributes);
325
427
  }
326
428
 
327
429
  // Record estimated overpayment if available
328
430
  if (strategyResult.estimatedOverpaymentEth !== undefined) {
329
- this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, strategyAttributes);
431
+ const overpaymentAttributes = {
432
+ ...strategyAttributes,
433
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
434
+ };
435
+ this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, overpaymentAttributes);
330
436
  }
331
437
  }
332
438
  }