@aztec/sequencer-client 0.0.1-commit.934299a21 → 0.0.1-commit.949a33fd8

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 (84) hide show
  1. package/dest/client/sequencer-client.d.ts +6 -1
  2. package/dest/client/sequencer-client.d.ts.map +1 -1
  3. package/dest/client/sequencer-client.js +49 -27
  4. package/dest/config.d.ts +25 -5
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +41 -26
  7. package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
  8. package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
  9. package/dest/global_variable_builder/fee_predictor.js +128 -0
  10. package/dest/global_variable_builder/fee_provider.d.ts +21 -0
  11. package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
  12. package/dest/global_variable_builder/fee_provider.js +58 -0
  13. package/dest/global_variable_builder/global_builder.d.ts +15 -14
  14. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  15. package/dest/global_variable_builder/global_builder.js +16 -50
  16. package/dest/global_variable_builder/index.d.ts +4 -2
  17. package/dest/global_variable_builder/index.d.ts.map +1 -1
  18. package/dest/global_variable_builder/index.js +2 -0
  19. package/dest/publisher/config.d.ts +13 -1
  20. package/dest/publisher/config.d.ts.map +1 -1
  21. package/dest/publisher/config.js +19 -4
  22. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +3 -4
  23. package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -1
  24. package/dest/publisher/sequencer-publisher-factory.d.ts +3 -5
  25. package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
  26. package/dest/publisher/sequencer-publisher-factory.js +16 -3
  27. package/dest/publisher/sequencer-publisher.d.ts +55 -43
  28. package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
  29. package/dest/publisher/sequencer-publisher.js +182 -118
  30. package/dest/sequencer/chain_state_overrides.d.ts +25 -0
  31. package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
  32. package/dest/sequencer/chain_state_overrides.js +39 -0
  33. package/dest/sequencer/checkpoint_proposal_job.d.ts +46 -10
  34. package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
  35. package/dest/sequencer/checkpoint_proposal_job.js +671 -225
  36. package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
  37. package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
  38. package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
  39. package/dest/sequencer/checkpoint_voter.d.ts +1 -2
  40. package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
  41. package/dest/sequencer/checkpoint_voter.js +2 -5
  42. package/dest/sequencer/events.d.ts +7 -1
  43. package/dest/sequencer/events.d.ts.map +1 -1
  44. package/dest/sequencer/metrics.d.ts +13 -10
  45. package/dest/sequencer/metrics.d.ts.map +1 -1
  46. package/dest/sequencer/metrics.js +45 -20
  47. package/dest/sequencer/sequencer.d.ts +38 -13
  48. package/dest/sequencer/sequencer.d.ts.map +1 -1
  49. package/dest/sequencer/sequencer.js +167 -79
  50. package/dest/sequencer/timetable.d.ts +17 -3
  51. package/dest/sequencer/timetable.d.ts.map +1 -1
  52. package/dest/sequencer/timetable.js +51 -43
  53. package/dest/sequencer/types.d.ts +2 -2
  54. package/dest/sequencer/types.d.ts.map +1 -1
  55. package/dest/test/mock_checkpoint_builder.d.ts +7 -9
  56. package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
  57. package/dest/test/mock_checkpoint_builder.js +39 -30
  58. package/dest/test/utils.d.ts +1 -1
  59. package/dest/test/utils.d.ts.map +1 -1
  60. package/dest/test/utils.js +7 -6
  61. package/package.json +27 -28
  62. package/src/client/sequencer-client.ts +61 -28
  63. package/src/config.ts +49 -27
  64. package/src/global_variable_builder/README.md +44 -0
  65. package/src/global_variable_builder/fee_predictor.ts +172 -0
  66. package/src/global_variable_builder/fee_provider.ts +75 -0
  67. package/src/global_variable_builder/global_builder.ts +26 -62
  68. package/src/global_variable_builder/index.ts +3 -1
  69. package/src/publisher/config.ts +38 -4
  70. package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +3 -1
  71. package/src/publisher/sequencer-publisher-factory.ts +18 -6
  72. package/src/publisher/sequencer-publisher.ts +265 -171
  73. package/src/sequencer/README.md +82 -13
  74. package/src/sequencer/chain_state_overrides.ts +87 -0
  75. package/src/sequencer/checkpoint_proposal_job.ts +823 -251
  76. package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
  77. package/src/sequencer/checkpoint_voter.ts +1 -12
  78. package/src/sequencer/events.ts +6 -1
  79. package/src/sequencer/metrics.ts +57 -24
  80. package/src/sequencer/sequencer.ts +233 -88
  81. package/src/sequencer/timetable.ts +64 -52
  82. package/src/sequencer/types.ts +1 -1
  83. package/src/test/mock_checkpoint_builder.ts +51 -48
  84. package/src/test/utils.ts +28 -10
@@ -0,0 +1,128 @@
1
+ import {
2
+ Attributes,
3
+ type Gauge,
4
+ type Histogram,
5
+ type Meter,
6
+ Metrics,
7
+ type TelemetryClient,
8
+ } from '@aztec/telemetry-client';
9
+
10
+ type CheckpointProposalJobInstruments = {
11
+ checkpointAttestationDelay: Histogram;
12
+ checkpointBuildDuration: Histogram;
13
+ checkpointStartToFirstBlockDuration: Histogram;
14
+ checkpointLastBlockToBroadcastDuration: Histogram;
15
+ pipelinedCheckpointBuildStartOffsetFromSlotBoundary: Histogram;
16
+ checkpointBlockCount: Gauge;
17
+ checkpointTxCount: Gauge;
18
+ checkpointTotalMana: Gauge;
19
+ };
20
+
21
+ /**
22
+ * Per-job recording surface used by {@link CheckpointProposalJob}.
23
+ *
24
+ * Ownership split:
25
+ * - {@link CheckpointProposalJobMetrics} owns the OpenTelemetry meter and instruments and should live for the
26
+ * lifetime of the sequencer process.
27
+ * - A recorder owns only mutable timing state for one checkpoint proposal job so overlapping jobs cannot
28
+ * overwrite each other's timing markers.
29
+ */
30
+ export interface CheckpointProposalJobMetricsRecorder {
31
+ recordCheckpointAttestationDelay(durationMs: number): void;
32
+ recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number): void;
33
+ recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(offsetMs: number): void;
34
+ startCheckpointTiming(nowMs: number): void;
35
+ noteCheckpointBlockBuilt(nowMs: number, opts: { isFirstBlock: boolean; isLastBlock: boolean }): void;
36
+ noteCheckpointBroadcast(nowMs: number): void;
37
+ }
38
+
39
+ /**
40
+ * Concrete per-job recorder.
41
+ *
42
+ * This class should be short-lived: create one recorder per checkpoint proposal job and discard it when the job
43
+ * completes. It intentionally does not create instruments; it only holds the job-local timestamps needed to derive
44
+ * timing metrics safely.
45
+ */
46
+ class CheckpointProposalJobMetricsRecorderImpl implements CheckpointProposalJobMetricsRecorder {
47
+ private checkpointStartedAt?: number;
48
+ private checkpointLastBlockBuiltAt?: number;
49
+
50
+ constructor(private readonly instruments: CheckpointProposalJobInstruments) {}
51
+
52
+ public recordCheckpointAttestationDelay(durationMs: number) {
53
+ this.instruments.checkpointAttestationDelay.record(Math.ceil(durationMs));
54
+ }
55
+
56
+ public recordCheckpointBuild(durationMs: number, blockCount: number, txCount: number, totalMana: number) {
57
+ this.instruments.checkpointBuildDuration.record(Math.ceil(durationMs));
58
+ this.instruments.checkpointBlockCount.record(blockCount);
59
+ this.instruments.checkpointTxCount.record(txCount);
60
+ this.instruments.checkpointTotalMana.record(totalMana);
61
+ }
62
+
63
+ public recordPipelinedCheckpointBuildStartOffsetFromSlotBoundary(offsetMs: number) {
64
+ this.instruments.pipelinedCheckpointBuildStartOffsetFromSlotBoundary.record(Math.ceil(Math.abs(offsetMs)), {
65
+ [Attributes.SLOT_BOUNDARY_SIDE]: offsetMs < 0 ? 'before' : 'after',
66
+ });
67
+ }
68
+
69
+ public startCheckpointTiming(nowMs: number) {
70
+ this.checkpointStartedAt = nowMs;
71
+ this.checkpointLastBlockBuiltAt = undefined;
72
+ }
73
+
74
+ public noteCheckpointBlockBuilt(nowMs: number, opts: { isFirstBlock: boolean; isLastBlock: boolean }) {
75
+ if (opts.isFirstBlock && this.checkpointStartedAt !== undefined) {
76
+ this.instruments.checkpointStartToFirstBlockDuration.record(Math.ceil(nowMs - this.checkpointStartedAt));
77
+ }
78
+
79
+ if (opts.isLastBlock) {
80
+ this.checkpointLastBlockBuiltAt = nowMs;
81
+ }
82
+ }
83
+
84
+ public noteCheckpointBroadcast(nowMs: number) {
85
+ if (this.checkpointLastBlockBuiltAt !== undefined) {
86
+ this.instruments.checkpointLastBlockToBroadcastDuration.record(
87
+ Math.ceil(nowMs - this.checkpointLastBlockBuiltAt),
88
+ );
89
+ }
90
+
91
+ this.checkpointLastBlockBuiltAt = undefined;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Long-lived owner of checkpoint proposal job telemetry instruments.
97
+ *
98
+ * The sequencer should construct this once and reuse it across jobs. Each call to {@link createRecorder} returns a
99
+ * lightweight recorder with isolated mutable state for a single checkpoint proposal job.
100
+ */
101
+ export class CheckpointProposalJobMetrics {
102
+ private readonly meter: Meter;
103
+ private readonly instruments: CheckpointProposalJobInstruments;
104
+
105
+ constructor(client: TelemetryClient, name = 'CheckpointProposalJob') {
106
+ this.meter = client.getMeter(name);
107
+ this.instruments = {
108
+ checkpointAttestationDelay: this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY),
109
+ checkpointBuildDuration: this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_BUILD_DURATION),
110
+ checkpointStartToFirstBlockDuration: this.meter.createHistogram(
111
+ Metrics.SEQUENCER_CHECKPOINT_START_TO_FIRST_BLOCK_DURATION,
112
+ ),
113
+ checkpointLastBlockToBroadcastDuration: this.meter.createHistogram(
114
+ Metrics.SEQUENCER_CHECKPOINT_LAST_BLOCK_TO_BROADCAST_DURATION,
115
+ ),
116
+ pipelinedCheckpointBuildStartOffsetFromSlotBoundary: this.meter.createHistogram(
117
+ Metrics.SEQUENCER_PIPELINED_CHECKPOINT_BUILD_START_OFFSET_FROM_SLOT_BOUNDARY,
118
+ ),
119
+ checkpointBlockCount: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_BLOCK_COUNT),
120
+ checkpointTxCount: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TX_COUNT),
121
+ checkpointTotalMana: this.meter.createGauge(Metrics.SEQUENCER_CHECKPOINT_TOTAL_MANA),
122
+ };
123
+ }
124
+
125
+ public createRecorder(): CheckpointProposalJobMetricsRecorder {
126
+ return new CheckpointProposalJobMetricsRecorderImpl(this.instruments);
127
+ }
128
+ }
@@ -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;
@@ -24,4 +24,9 @@ export type SequencerEvents = {
24
24
  }) => void;
25
25
  ['checkpoint-published']: (args: { checkpoint: CheckpointNumber; slot: SlotNumber }) => void;
26
26
  ['checkpoint-error']: (args: { error: Error }) => void;
27
+ ['pipelined-checkpoint-discarded']: (args: {
28
+ slot: SlotNumber;
29
+ checkpointNumber: CheckpointNumber;
30
+ reason: string;
31
+ }) => void;
27
32
  };
@@ -26,6 +26,7 @@ export class SequencerMetrics {
26
26
  private blockBuildDuration: Histogram;
27
27
  private blockBuildManaPerSecond: Gauge;
28
28
  private stateTransitionBufferDuration: Histogram;
29
+ private stateDuration: Histogram;
29
30
 
30
31
  // these are gauges because for individual sequencers building a block is not something that happens often enough to warrant a histogram
31
32
  private timeToCollectAttestations: Gauge;
@@ -44,11 +45,9 @@ export class SequencerMetrics {
44
45
  private checkpointProposalFailed: UpDownCounter;
45
46
  private checkpointSuccess: UpDownCounter;
46
47
  private slashingAttempts: UpDownCounter;
47
- private checkpointAttestationDelay: Histogram;
48
- private checkpointBuildDuration: Histogram;
49
- private checkpointBlockCount: Gauge;
50
- private checkpointTxCount: Gauge;
51
- private checkpointTotalMana: Gauge;
48
+ private pipelineDepth: Gauge;
49
+ private pipelineDiscards: UpDownCounter;
50
+ private pipelineParentCheckpointMismatches: UpDownCounter;
52
51
 
53
52
  // Fisherman fee analysis metrics
54
53
  private fishermanWouldBeIncluded: UpDownCounter;
@@ -66,6 +65,10 @@ export class SequencerMetrics {
66
65
  private fishermanMinedBlobTxPriorityFee: Histogram;
67
66
  private fishermanMinedBlobTxTotalCost: Histogram;
68
67
 
68
+ private blockInterBlockTime: Histogram;
69
+ private lastBlockBuiltTimestamp?: number;
70
+ private lastBlockBuiltSlot?: SlotNumber;
71
+
69
72
  private lastSeenSlot?: SlotNumber;
70
73
 
71
74
  constructor(
@@ -84,9 +87,11 @@ export class SequencerMetrics {
84
87
 
85
88
  this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
86
89
 
90
+ this.blockInterBlockTime = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_INTER_BLOCK_TIME);
91
+
87
92
  this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
88
93
 
89
- this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
94
+ this.stateDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_DURATION);
90
95
 
91
96
  this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
92
97
 
@@ -136,13 +141,25 @@ export class SequencerMetrics {
136
141
  Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
137
142
  );
138
143
 
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
144
  this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
145
145
 
146
+ this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
147
+ this.pipelineDiscards = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_PIPELINE_DISCARDS_COUNT);
148
+ this.pipelineParentCheckpointMismatches = createUpDownCounterWithDefault(
149
+ this.meter,
150
+ Metrics.SEQUENCER_PIPELINE_PARENT_CHECKPOINT_MISMATCH_COUNT,
151
+ {
152
+ [Attributes.ERROR_TYPE]: [
153
+ 'archiver-sync-timeout',
154
+ 'parent-not-on-l1',
155
+ 'parent-hash-mismatch',
156
+ 'parent-invalid-attestations',
157
+ 'unexpected-parent-appeared',
158
+ ],
159
+ },
160
+ );
161
+ this.pipelineDepth.record(0);
162
+
146
163
  // Fisherman fee analysis metrics
147
164
  this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
148
165
  this.meter,
@@ -205,21 +222,25 @@ export class SequencerMetrics {
205
222
  this.timeToCollectAttestations.record(0);
206
223
  }
207
224
 
208
- public recordCheckpointAttestationDelay(duration: number) {
209
- this.checkpointAttestationDelay.record(duration);
210
- }
211
-
212
225
  public recordCollectedAttestations(count: number, durationMs: number) {
213
226
  this.collectedAttestions.record(count);
214
227
  this.timeToCollectAttestations.record(Math.ceil(durationMs));
215
228
  }
216
229
 
217
- recordBuiltBlock(buildDurationMs: number, totalMana: number) {
230
+ recordBuiltBlock(buildDurationMs: number, totalMana: number, slot: SlotNumber) {
218
231
  this.blockCounter.add(1, {
219
232
  [Attributes.STATUS]: 'built',
220
233
  });
221
234
  this.blockBuildDuration.record(Math.ceil(buildDurationMs));
222
235
  this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
236
+
237
+ // Only record inter-block time between blocks built within the same slot.
238
+ const now = Date.now();
239
+ if (this.lastBlockBuiltTimestamp !== undefined && this.lastBlockBuiltSlot === slot) {
240
+ this.blockInterBlockTime.record(now - this.lastBlockBuiltTimestamp);
241
+ }
242
+ this.lastBlockBuiltTimestamp = now;
243
+ this.lastBlockBuiltSlot = slot;
223
244
  }
224
245
 
225
246
  recordFailedBlock() {
@@ -234,6 +255,26 @@ export class SequencerMetrics {
234
255
  });
235
256
  }
236
257
 
258
+ recordStateDuration(durationMs: number, state: SequencerState) {
259
+ this.stateDuration.record(Math.ceil(durationMs), {
260
+ [Attributes.SEQUENCER_STATE]: state,
261
+ });
262
+ }
263
+
264
+ recordPipelineDepth(depth: number) {
265
+ this.pipelineDepth.record(depth);
266
+ }
267
+
268
+ recordPipelineDiscard(count = 1) {
269
+ this.pipelineDiscards.add(count);
270
+ }
271
+
272
+ recordPipelineParentCheckpointMismatch(reason: string) {
273
+ this.pipelineParentCheckpointMismatches.add(1, {
274
+ [Attributes.ERROR_TYPE]: reason,
275
+ });
276
+ }
277
+
237
278
  incOpenSlot(slot: SlotNumber, proposer: string) {
238
279
  // sequencer went through the loop a second time. Noop
239
280
  if (slot === this.lastSeenSlot) {
@@ -292,14 +333,6 @@ export class SequencerMetrics {
292
333
  });
293
334
  }
294
335
 
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
336
  recordSlashingAttempt(actionCount: number) {
304
337
  this.slashingAttempts.add(actionCount);
305
338
  }