@aztec/sequencer-client 0.0.0-test.1 → 0.0.1-commit.001888fc

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 (151) hide show
  1. package/dest/client/index.d.ts +1 -1
  2. package/dest/client/sequencer-client.d.ts +50 -33
  3. package/dest/client/sequencer-client.d.ts.map +1 -1
  4. package/dest/client/sequencer-client.js +164 -57
  5. package/dest/config.d.ts +35 -17
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +150 -81
  8. package/dest/global_variable_builder/global_builder.d.ts +24 -15
  9. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  10. package/dest/global_variable_builder/global_builder.js +63 -44
  11. package/dest/global_variable_builder/index.d.ts +1 -1
  12. package/dest/index.d.ts +2 -4
  13. package/dest/index.d.ts.map +1 -1
  14. package/dest/index.js +1 -3
  15. package/dest/publisher/config.d.ts +48 -27
  16. package/dest/publisher/config.d.ts.map +1 -1
  17. package/dest/publisher/config.js +109 -32
  18. package/dest/publisher/index.d.ts +4 -1
  19. package/dest/publisher/index.d.ts.map +1 -1
  20. package/dest/publisher/index.js +3 -0
  21. package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
  22. package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
  23. package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
  24. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
  25. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
  26. package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
  27. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
  28. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
  29. package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
  30. package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
  31. package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
  32. package/dest/publisher/l1_tx_failed_store/index.js +2 -0
  33. package/dest/publisher/sequencer-publisher-factory.d.ts +52 -0
  34. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -0
  35. package/dest/publisher/sequencer-publisher-factory.js +76 -0
  36. package/dest/publisher/sequencer-publisher-metrics.d.ts +5 -4
  37. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
  38. package/dest/publisher/sequencer-publisher-metrics.js +34 -62
  39. package/dest/publisher/sequencer-publisher.d.ts +158 -93
  40. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  41. package/dest/publisher/sequencer-publisher.js +1452 -255
  42. package/dest/sequencer/checkpoint_proposal_job.d.ts +100 -0
  43. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
  44. package/dest/sequencer/checkpoint_proposal_job.js +1240 -0
  45. package/dest/sequencer/checkpoint_voter.d.ts +35 -0
  46. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -0
  47. package/dest/sequencer/checkpoint_voter.js +109 -0
  48. package/dest/sequencer/config.d.ts +7 -1
  49. package/dest/sequencer/config.d.ts.map +1 -1
  50. package/dest/sequencer/errors.d.ts +11 -0
  51. package/dest/sequencer/errors.d.ts.map +1 -0
  52. package/dest/sequencer/errors.js +15 -0
  53. package/dest/sequencer/events.d.ts +46 -0
  54. package/dest/sequencer/events.d.ts.map +1 -0
  55. package/dest/sequencer/events.js +1 -0
  56. package/dest/sequencer/index.d.ts +4 -2
  57. package/dest/sequencer/index.d.ts.map +1 -1
  58. package/dest/sequencer/index.js +3 -1
  59. package/dest/sequencer/metrics.d.ts +60 -12
  60. package/dest/sequencer/metrics.d.ts.map +1 -1
  61. package/dest/sequencer/metrics.js +283 -67
  62. package/dest/sequencer/sequencer.d.ts +157 -135
  63. package/dest/sequencer/sequencer.d.ts.map +1 -1
  64. package/dest/sequencer/sequencer.js +966 -523
  65. package/dest/sequencer/timetable.d.ts +75 -25
  66. package/dest/sequencer/timetable.d.ts.map +1 -1
  67. package/dest/sequencer/timetable.js +174 -62
  68. package/dest/sequencer/types.d.ts +3 -0
  69. package/dest/sequencer/types.d.ts.map +1 -0
  70. package/dest/sequencer/types.js +1 -0
  71. package/dest/sequencer/utils.d.ts +20 -38
  72. package/dest/sequencer/utils.d.ts.map +1 -1
  73. package/dest/sequencer/utils.js +12 -47
  74. package/dest/test/index.d.ts +9 -3
  75. package/dest/test/index.d.ts.map +1 -1
  76. package/dest/test/index.js +0 -4
  77. package/dest/test/mock_checkpoint_builder.d.ts +99 -0
  78. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -0
  79. package/dest/test/mock_checkpoint_builder.js +231 -0
  80. package/dest/test/utils.d.ts +53 -0
  81. package/dest/test/utils.d.ts.map +1 -0
  82. package/dest/test/utils.js +104 -0
  83. package/package.json +46 -45
  84. package/src/client/sequencer-client.ts +220 -104
  85. package/src/config.ts +172 -96
  86. package/src/global_variable_builder/global_builder.ts +85 -55
  87. package/src/index.ts +1 -3
  88. package/src/publisher/config.ts +150 -59
  89. package/src/publisher/index.ts +7 -0
  90. package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
  91. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
  92. package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
  93. package/src/publisher/l1_tx_failed_store/index.ts +3 -0
  94. package/src/publisher/sequencer-publisher-factory.ts +124 -0
  95. package/src/publisher/sequencer-publisher-metrics.ts +33 -63
  96. package/src/publisher/sequencer-publisher.ts +1266 -311
  97. package/src/sequencer/README.md +531 -0
  98. package/src/sequencer/checkpoint_proposal_job.ts +967 -0
  99. package/src/sequencer/checkpoint_voter.ts +130 -0
  100. package/src/sequencer/config.ts +8 -0
  101. package/src/sequencer/errors.ts +21 -0
  102. package/src/sequencer/events.ts +27 -0
  103. package/src/sequencer/index.ts +3 -1
  104. package/src/sequencer/metrics.ts +362 -73
  105. package/src/sequencer/sequencer.ts +712 -586
  106. package/src/sequencer/timetable.ts +224 -64
  107. package/src/sequencer/types.ts +6 -0
  108. package/src/sequencer/utils.ts +28 -60
  109. package/src/test/index.ts +11 -5
  110. package/src/test/mock_checkpoint_builder.ts +323 -0
  111. package/src/test/utils.ts +167 -0
  112. package/dest/sequencer/allowed.d.ts +0 -3
  113. package/dest/sequencer/allowed.d.ts.map +0 -1
  114. package/dest/sequencer/allowed.js +0 -27
  115. package/dest/slasher/factory.d.ts +0 -7
  116. package/dest/slasher/factory.d.ts.map +0 -1
  117. package/dest/slasher/factory.js +0 -8
  118. package/dest/slasher/index.d.ts +0 -3
  119. package/dest/slasher/index.d.ts.map +0 -1
  120. package/dest/slasher/index.js +0 -2
  121. package/dest/slasher/slasher_client.d.ts +0 -75
  122. package/dest/slasher/slasher_client.d.ts.map +0 -1
  123. package/dest/slasher/slasher_client.js +0 -132
  124. package/dest/tx_validator/archive_cache.d.ts +0 -14
  125. package/dest/tx_validator/archive_cache.d.ts.map +0 -1
  126. package/dest/tx_validator/archive_cache.js +0 -22
  127. package/dest/tx_validator/gas_validator.d.ts +0 -14
  128. package/dest/tx_validator/gas_validator.d.ts.map +0 -1
  129. package/dest/tx_validator/gas_validator.js +0 -78
  130. package/dest/tx_validator/nullifier_cache.d.ts +0 -16
  131. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  132. package/dest/tx_validator/nullifier_cache.js +0 -24
  133. package/dest/tx_validator/phases_validator.d.ts +0 -12
  134. package/dest/tx_validator/phases_validator.d.ts.map +0 -1
  135. package/dest/tx_validator/phases_validator.js +0 -80
  136. package/dest/tx_validator/test_utils.d.ts +0 -23
  137. package/dest/tx_validator/test_utils.d.ts.map +0 -1
  138. package/dest/tx_validator/test_utils.js +0 -26
  139. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  140. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  141. package/dest/tx_validator/tx_validator_factory.js +0 -50
  142. package/src/sequencer/allowed.ts +0 -36
  143. package/src/slasher/factory.ts +0 -15
  144. package/src/slasher/index.ts +0 -2
  145. package/src/slasher/slasher_client.ts +0 -193
  146. package/src/tx_validator/archive_cache.ts +0 -28
  147. package/src/tx_validator/gas_validator.ts +0 -101
  148. package/src/tx_validator/nullifier_cache.ts +0 -30
  149. package/src/tx_validator/phases_validator.ts +0 -98
  150. package/src/tx_validator/test_utils.ts +0 -48
  151. package/src/tx_validator/tx_validator_factory.ts +0 -120
@@ -1,108 +1,217 @@
1
+ import { EthAddress } from '@aztec/aztec.js/addresses';
2
+ import type { RollupContract } from '@aztec/ethereum/contracts';
3
+ import type { L1FeeAnalysisResult } from '@aztec/ethereum/l1-fee-analysis';
4
+ import type { SlotNumber } from '@aztec/foundation/branded-types';
1
5
  import {
2
6
  Attributes,
3
7
  type Gauge,
4
8
  type Histogram,
9
+ type Meter,
5
10
  Metrics,
6
11
  type TelemetryClient,
7
12
  type Tracer,
8
13
  type UpDownCounter,
9
- ValueType,
14
+ createUpDownCounterWithDefault,
10
15
  } from '@aztec/telemetry-client';
11
16
 
12
- import { type SequencerState, type SequencerStateCallback, sequencerStateToNumber } from './utils.js';
17
+ import { type Hex, formatUnits } from 'viem';
18
+
19
+ import type { SequencerState } from './utils.js';
13
20
 
14
21
  export class SequencerMetrics {
15
22
  public readonly tracer: Tracer;
23
+ private meter: Meter;
16
24
 
17
25
  private blockCounter: UpDownCounter;
18
26
  private blockBuildDuration: Histogram;
19
27
  private blockBuildManaPerSecond: Gauge;
20
28
  private stateTransitionBufferDuration: Histogram;
21
- private currentBlockNumber: Gauge;
22
- private currentBlockSize: Gauge;
23
- private blockBuilderInsertions: Histogram;
24
29
 
30
+ // these are gauges because for individual sequencers building a block is not something that happens often enough to warrant a histogram
25
31
  private timeToCollectAttestations: Gauge;
32
+ private allowanceToCollectAttestations: Gauge;
33
+ private requiredAttestions: Gauge;
34
+ private collectedAttestions: Gauge;
26
35
 
27
- constructor(client: TelemetryClient, getState: SequencerStateCallback, name = 'Sequencer') {
28
- const meter = client.getMeter(name);
29
- this.tracer = client.getTracer(name);
36
+ private rewards: Gauge;
30
37
 
31
- this.blockCounter = meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
38
+ private slots: UpDownCounter;
39
+ private filledSlots: UpDownCounter;
32
40
 
33
- this.blockBuildDuration = meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION, {
34
- unit: 'ms',
35
- description: 'Duration to build a block',
36
- valueType: ValueType.INT,
37
- });
41
+ private blockProposalFailed: UpDownCounter;
42
+ private checkpointProposalSuccess: UpDownCounter;
43
+ private checkpointPrecheckFailed: UpDownCounter;
44
+ private checkpointProposalFailed: UpDownCounter;
45
+ private checkpointSuccess: UpDownCounter;
46
+ private slashingAttempts: UpDownCounter;
47
+ private checkpointAttestationDelay: Histogram;
48
+ private checkpointBuildDuration: Histogram;
49
+ private checkpointBlockCount: Gauge;
50
+ private checkpointTxCount: Gauge;
51
+ private checkpointTotalMana: Gauge;
38
52
 
39
- this.blockBuildManaPerSecond = meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND, {
40
- unit: 'mana/s',
41
- description: 'Mana per second when building a block',
42
- valueType: ValueType.INT,
43
- });
53
+ // Fisherman fee analysis metrics
54
+ private fishermanWouldBeIncluded: UpDownCounter;
55
+ private fishermanTimeBeforeBlock: Histogram;
56
+ private fishermanPendingBlobTxCount: Histogram;
57
+ private fishermanIncludedBlobTxCount: Histogram;
58
+ private fishermanPendingBlobCount: Histogram;
59
+ private fishermanIncludedBlobCount: Histogram;
60
+ private fishermanBlockBlobsFull: UpDownCounter;
61
+ private fishermanMaxBlobCapacity: Histogram;
62
+ private fishermanCalculatedPriorityFee: Histogram;
63
+ private fishermanPriorityFeeDelta: Histogram;
64
+ private fishermanEstimatedCost: Histogram;
65
+ private fishermanEstimatedOverpayment: Histogram;
66
+ private fishermanMinedBlobTxPriorityFee: Histogram;
67
+ private fishermanMinedBlobTxTotalCost: Histogram;
44
68
 
45
- this.stateTransitionBufferDuration = meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION, {
46
- unit: 'ms',
47
- description:
48
- 'The time difference between when the sequencer needed to transition to a new state and when it actually did.',
49
- valueType: ValueType.INT,
50
- });
69
+ private lastSeenSlot?: SlotNumber;
51
70
 
52
- const currentState = meter.createObservableGauge(Metrics.SEQUENCER_CURRENT_STATE, {
53
- description: 'Current state of the sequencer',
54
- });
71
+ constructor(
72
+ client: TelemetryClient,
73
+ private rollup: RollupContract,
74
+ name = 'Sequencer',
75
+ ) {
76
+ this.meter = client.getMeter(name);
77
+ this.tracer = client.getTracer(name);
55
78
 
56
- currentState.addCallback(observer => {
57
- observer.observe(sequencerStateToNumber(getState()));
79
+ this.blockCounter = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_COUNT, {
80
+ [Attributes.STATUS]: ['failed', 'built'],
58
81
  });
59
82
 
60
- this.currentBlockNumber = meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_NUMBER, {
61
- description: 'Current block number',
62
- valueType: ValueType.INT,
63
- });
83
+ this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
64
84
 
65
- this.currentBlockSize = meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_SIZE, {
66
- description: 'Current block size',
67
- valueType: ValueType.INT,
68
- });
85
+ this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
69
86
 
70
- this.timeToCollectAttestations = meter.createGauge(Metrics.SEQUENCER_TIME_TO_COLLECT_ATTESTATIONS, {
71
- description: 'The time spent collecting attestations from committee members',
72
- valueType: ValueType.INT,
73
- });
87
+ this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
74
88
 
75
- this.blockBuilderInsertions = meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_INSERTION_TIME, {
76
- description: 'Timer for tree insertions performed by the block builder',
77
- unit: 'us',
78
- valueType: ValueType.INT,
79
- });
89
+ this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
80
90
 
81
- this.setCurrentBlock(0, 0);
82
- }
91
+ this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
92
+
93
+ this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
94
+
95
+ /**
96
+ * NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
97
+ * Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
98
+ */
99
+ this.filledSlots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_FILLED_SLOT_COUNT);
100
+
101
+ this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
102
+
103
+ this.allowanceToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_TIME_ALLOWANCE);
104
+
105
+ this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT);
106
+
107
+ this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
108
+
109
+ this.blockProposalFailed = createUpDownCounterWithDefault(
110
+ this.meter,
111
+ Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT,
112
+ );
113
+
114
+ this.checkpointProposalSuccess = createUpDownCounterWithDefault(
115
+ this.meter,
116
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_SUCCESS_COUNT,
117
+ );
118
+
119
+ this.checkpointSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
120
+
121
+ this.checkpointPrecheckFailed = createUpDownCounterWithDefault(
122
+ this.meter,
123
+ Metrics.SEQUENCER_CHECKPOINT_PRECHECK_FAILED_COUNT,
124
+ {
125
+ [Attributes.ERROR_TYPE]: [
126
+ 'slot_already_taken',
127
+ 'rollup_contract_check_failed',
128
+ 'slot_mismatch',
129
+ 'block_number_mismatch',
130
+ ],
131
+ },
132
+ );
83
133
 
84
- startCollectingAttestationsTimer(): () => void {
85
- const startTime = Date.now();
86
- const stop = () => {
87
- const duration = Date.now() - startTime;
88
- this.recordTimeToCollectAttestations(duration);
89
- };
90
- return stop.bind(this);
134
+ this.checkpointProposalFailed = createUpDownCounterWithDefault(
135
+ this.meter,
136
+ Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
137
+ );
138
+
139
+ this.checkpointBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION);
140
+ this.checkpointBlockCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT);
141
+ this.checkpointTxCount = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT);
142
+ this.checkpointTotalMana = this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA);
143
+
144
+ this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
145
+
146
+ // Fisherman fee analysis metrics
147
+ this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
148
+ this.meter,
149
+ Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED,
150
+ {
151
+ [Attributes.OK]: [true, false],
152
+ [Attributes.BLOCK_FULL]: ['true', 'false'],
153
+ },
154
+ );
155
+
156
+ this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
157
+
158
+ this.fishermanPendingBlobTxCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_TX_COUNT);
159
+
160
+ this.fishermanIncludedBlobTxCount = this.meter.createHistogram(
161
+ Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_TX_COUNT,
162
+ );
163
+
164
+ this.fishermanCalculatedPriorityFee = this.meter.createHistogram(
165
+ Metrics.FISHERMAN_FEE_ANALYSIS_CALCULATED_PRIORITY_FEE,
166
+ );
167
+
168
+ this.fishermanPriorityFeeDelta = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PRIORITY_FEE_DELTA);
169
+
170
+ this.fishermanEstimatedCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_COST);
171
+
172
+ this.fishermanEstimatedOverpayment = this.meter.createHistogram(
173
+ Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_OVERPAYMENT,
174
+ );
175
+
176
+ this.fishermanMinedBlobTxPriorityFee = this.meter.createHistogram(
177
+ Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_PRIORITY_FEE,
178
+ );
179
+
180
+ this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
181
+ Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
182
+ );
183
+
184
+ this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
185
+
186
+ this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
187
+
188
+ this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(
189
+ this.meter,
190
+ Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL,
191
+ {
192
+ [Attributes.OK]: [true, false],
193
+ },
194
+ );
195
+
196
+ this.fishermanMaxBlobCapacity = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MAX_BLOB_CAPACITY);
91
197
  }
92
198
 
93
- recordTimeToCollectAttestations(time: number) {
94
- this.timeToCollectAttestations.record(time);
199
+ public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
200
+ this.requiredAttestions.record(requiredAttestationsCount);
201
+ this.allowanceToCollectAttestations.record(Math.ceil(allowanceMs));
202
+
203
+ // reset
204
+ this.collectedAttestions.record(0);
205
+ this.timeToCollectAttestations.record(0);
95
206
  }
96
207
 
97
- recordBlockBuilderTreeInsertions(timeUs: number) {
98
- this.blockBuilderInsertions.record(Math.ceil(timeUs));
208
+ public recordCheckpointAttestationDelay(duration: number) {
209
+ this.checkpointAttestationDelay.record(duration);
99
210
  }
100
211
 
101
- recordCancelledBlock() {
102
- this.blockCounter.add(1, {
103
- [Attributes.STATUS]: 'cancelled',
104
- });
105
- this.setCurrentBlock(0, 0);
212
+ public recordCollectedAttestations(count: number, durationMs: number) {
213
+ this.collectedAttestions.record(count);
214
+ this.timeToCollectAttestations.record(Math.ceil(durationMs));
106
215
  }
107
216
 
108
217
  recordBuiltBlock(buildDurationMs: number, totalMana: number) {
@@ -117,11 +226,6 @@ export class SequencerMetrics {
117
226
  this.blockCounter.add(1, {
118
227
  [Attributes.STATUS]: 'failed',
119
228
  });
120
- this.setCurrentBlock(0, 0);
121
- }
122
-
123
- recordNewBlock(blockNumber: number, txCount: number) {
124
- this.setCurrentBlock(blockNumber, txCount);
125
229
  }
126
230
 
127
231
  recordStateTransitionBufferMs(durationMs: number, state: SequencerState) {
@@ -130,8 +234,193 @@ export class SequencerMetrics {
130
234
  });
131
235
  }
132
236
 
133
- private setCurrentBlock(blockNumber: number, txCount: number) {
134
- this.currentBlockNumber.record(blockNumber);
135
- this.currentBlockSize.record(txCount);
237
+ incOpenSlot(slot: SlotNumber, proposer: string) {
238
+ // sequencer went through the loop a second time. Noop
239
+ if (slot === this.lastSeenSlot) {
240
+ return;
241
+ }
242
+
243
+ this.slots.add(1, {
244
+ [Attributes.BLOCK_PROPOSER]: proposer,
245
+ });
246
+
247
+ this.lastSeenSlot = slot;
248
+ }
249
+
250
+ async incFilledSlot(proposer: string, coinbase: Hex | EthAddress | undefined): Promise<void> {
251
+ this.filledSlots.add(1, {
252
+ [Attributes.BLOCK_PROPOSER]: proposer,
253
+ });
254
+ this.lastSeenSlot = undefined;
255
+
256
+ if (coinbase) {
257
+ try {
258
+ const rewards = await this.rollup.getSequencerRewards(coinbase);
259
+ const fmt = parseFloat(formatUnits(rewards, 18));
260
+ this.rewards.record(fmt, {
261
+ [Attributes.COINBASE]: coinbase.toString(),
262
+ });
263
+ } catch {
264
+ // no-op
265
+ }
266
+ }
267
+ }
268
+
269
+ recordCheckpointSuccess() {
270
+ this.checkpointSuccess.add(1);
271
+ }
272
+
273
+ recordBlockProposalFailed(reason?: string) {
274
+ this.blockProposalFailed.add(1, {
275
+ ...(reason && { [Attributes.ERROR_TYPE]: reason }),
276
+ });
277
+ }
278
+
279
+ recordCheckpointProposalSuccess() {
280
+ this.checkpointProposalSuccess.add(1);
281
+ }
282
+
283
+ recordCheckpointPrecheckFailed(
284
+ checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch',
285
+ ) {
286
+ this.checkpointPrecheckFailed.add(1, { [Attributes.ERROR_TYPE]: checkType });
287
+ }
288
+
289
+ recordCheckpointProposalFailed(reason?: string) {
290
+ this.checkpointProposalFailed.add(1, {
291
+ ...(reason && { [Attributes.ERROR_TYPE]: reason }),
292
+ });
293
+ }
294
+
295
+ /** Records aggregate metrics for a completed checkpoint build. */
296
+ recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
297
+ this.checkpointBuildDuration.record(Math.ceil(durationMs));
298
+ this.checkpointBlockCount.record(blockCount);
299
+ this.checkpointTxCount.record(txCount);
300
+ this.checkpointTotalMana.record(totalMana);
301
+ }
302
+
303
+ recordSlashingAttempt(actionCount: number) {
304
+ this.slashingAttempts.add(actionCount);
305
+ }
306
+
307
+ /**
308
+ * Records metrics for a completed fisherman fee analysis
309
+ * @param analysis - The completed fee analysis result
310
+ */
311
+ recordFishermanFeeAnalysis(analysis: L1FeeAnalysisResult) {
312
+ // In fisherman mode, we should always have strategy results
313
+ if (!analysis.computedPrices.strategyResults || analysis.computedPrices.strategyResults.length === 0) {
314
+ // This should never happen in fisherman mode - log an error
315
+ // We don't record metrics without strategy IDs as that defeats the purpose
316
+ throw new Error(
317
+ `No strategy results found in fisherman fee analysis ${analysis.id}. This indicates a bug in the fee analysis.`,
318
+ );
319
+ }
320
+
321
+ // Record metrics for each strategy separately
322
+ for (const strategyResult of analysis.computedPrices.strategyResults) {
323
+ const strategyAttributes = {
324
+ [Attributes.FISHERMAN_FEE_STRATEGY_ID]: strategyResult.strategyId,
325
+ };
326
+
327
+ // Record pending block snapshot data (once per strategy for comparison)
328
+ this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
329
+ this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
330
+
331
+ // Record mined block data if available
332
+ if (analysis.minedBlock) {
333
+ this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
334
+ this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
335
+
336
+ // Record actual fees from blob transactions in the mined block
337
+ for (const blobTx of analysis.minedBlock.includedBlobTxs) {
338
+ // Record priority fee per gas in Gwei
339
+ const priorityFeeGwei = Number(blobTx.maxPriorityFeePerGas) / 1e9;
340
+ this.fishermanMinedBlobTxPriorityFee.record(priorityFeeGwei, strategyAttributes);
341
+
342
+ // Calculate total cost in ETH
343
+ // Cost = (gas * (baseFee + priorityFee)) + (blobCount * blobGasPerBlob * blobBaseFee)
344
+ const baseFee = analysis.minedBlock.baseFeePerGas;
345
+ const effectiveGasPrice = baseFee + blobTx.maxPriorityFeePerGas;
346
+
347
+ // Calculate execution cost using actual gas limit from the transaction
348
+ const executionCost = blobTx.gas * effectiveGasPrice;
349
+
350
+ // Calculate blob cost using maxFeePerBlobGas * blobCount * GAS_PER_BLOB
351
+ const blobCost = blobTx.maxFeePerBlobGas * BigInt(blobTx.blobCount) * 131072n; // 128KB per blob
352
+
353
+ const totalCostWei = executionCost + blobCost;
354
+ const totalCostEth = Number(totalCostWei) / 1e18;
355
+
356
+ this.fishermanMinedBlobTxTotalCost.record(totalCostEth, strategyAttributes);
357
+ }
358
+ }
359
+
360
+ // Record the calculated priority fee for this strategy
361
+ const calculatedPriorityFeeGwei = Number(strategyResult.calculatedPriorityFee) / 1e9;
362
+ this.fishermanCalculatedPriorityFee.record(calculatedPriorityFeeGwei, strategyAttributes);
363
+
364
+ // Record analysis results if available
365
+ if (analysis.analysis) {
366
+ this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
367
+
368
+ // Record whether the block reached 100% blob capacity
369
+ if (analysis.analysis.blockBlobsFull) {
370
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: true });
371
+ } else {
372
+ this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: false });
373
+ }
374
+
375
+ // Record the max blob capacity for this block
376
+ this.fishermanMaxBlobCapacity.record(analysis.analysis.maxBlobCapacity, strategyAttributes);
377
+
378
+ // Record strategy-specific inclusion result
379
+ if (strategyResult.wouldBeIncluded !== undefined) {
380
+ const inclusionAttributes = {
381
+ ...strategyAttributes,
382
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
383
+ };
384
+
385
+ if (strategyResult.wouldBeIncluded) {
386
+ this.fishermanWouldBeIncluded.add(1, { ...inclusionAttributes, [Attributes.OK]: true });
387
+ } else {
388
+ this.fishermanWouldBeIncluded.add(1, {
389
+ ...inclusionAttributes,
390
+ [Attributes.OK]: false,
391
+ ...(strategyResult.exclusionReason && { [Attributes.ERROR_TYPE]: strategyResult.exclusionReason }),
392
+ });
393
+ }
394
+ }
395
+
396
+ // Record strategy-specific priority fee delta
397
+ if (strategyResult.priorityFeeDelta !== undefined) {
398
+ const priorityFeeDeltaGwei = Number(strategyResult.priorityFeeDelta) / 1e9;
399
+ const deltaAttributes = {
400
+ ...strategyAttributes,
401
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
402
+ };
403
+ this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, deltaAttributes);
404
+ }
405
+
406
+ // Record estimated cost if available
407
+ if (strategyResult.estimatedCostEth !== undefined) {
408
+ const costAttributes = {
409
+ ...strategyAttributes,
410
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
411
+ };
412
+ this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, costAttributes);
413
+ }
414
+
415
+ // Record estimated overpayment if available
416
+ if (strategyResult.estimatedOverpaymentEth !== undefined) {
417
+ const overpaymentAttributes = {
418
+ ...strategyAttributes,
419
+ [Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
420
+ };
421
+ this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, overpaymentAttributes);
422
+ }
423
+ }
424
+ }
136
425
  }
137
426
  }