@aztec/sequencer-client 2.0.3-rc.9 → 2.1.0-rc.1

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.
@@ -27,20 +27,19 @@ import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs'
27
27
  import { sumBigint } from '@aztec/foundation/bigint';
28
28
  import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
29
29
  import { EthAddress } from '@aztec/foundation/eth-address';
30
+ import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
30
31
  import type { Fr } from '@aztec/foundation/fields';
31
- import { createLogger } from '@aztec/foundation/log';
32
+ import { type Logger, createLogger } from '@aztec/foundation/log';
32
33
  import { bufferToHex } from '@aztec/foundation/string';
33
34
  import { DateProvider, Timer } from '@aztec/foundation/timer';
34
35
  import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
35
36
  import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
36
- import { CommitteeAttestation, type ValidateBlockResult } from '@aztec/stdlib/block';
37
+ import { CommitteeAttestation, CommitteeAttestationsAndSigners, type ValidateBlockResult } from '@aztec/stdlib/block';
37
38
  import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
38
- import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/stdlib/p2p';
39
39
  import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
40
- import { type ProposedBlockHeader, StateReference, TxHash } from '@aztec/stdlib/tx';
40
+ import { type ProposedBlockHeader, StateReference } from '@aztec/stdlib/tx';
41
41
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
42
42
 
43
- import pick from 'lodash.pick';
44
43
  import { type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
45
44
 
46
45
  import type { PublisherConfig, TxSenderConfig } from './config.js';
@@ -56,17 +55,12 @@ type L1ProcessArgs = {
56
55
  stateReference: StateReference;
57
56
  /** L2 block blobs containing all tx effects. */
58
57
  blobs: Blob[];
59
- /** L2 block tx hashes */
60
- txHashes: TxHash[];
61
58
  /** Attestations */
62
- attestations?: CommitteeAttestation[];
59
+ attestationsAndSigners: CommitteeAttestationsAndSigners;
60
+ /** Attestations and signers signature */
61
+ attestationsAndSignersSignature: Signature;
63
62
  };
64
63
 
65
- export enum SignalType {
66
- GOVERNANCE,
67
- SLASHING,
68
- }
69
-
70
64
  export const Actions = [
71
65
  'invalidate-by-invalid-attestation',
72
66
  'invalidate-by-insufficient-attestations',
@@ -78,8 +72,11 @@ export const Actions = [
78
72
  'vote-offenses',
79
73
  'execute-slash',
80
74
  ] as const;
75
+
81
76
  export type Action = (typeof Actions)[number];
82
77
 
78
+ type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slashing-signal'>;
79
+
83
80
  // Sorting for actions such that invalidations go before proposals, and proposals go before votes
84
81
  export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
85
82
 
@@ -104,6 +101,7 @@ interface RequestWithExpiry {
104
101
  }
105
102
 
106
103
  export class SequencerPublisher {
104
+ private enabled: boolean;
107
105
  private interrupted = false;
108
106
  private metrics: SequencerPublisherMetrics;
109
107
  public epochCache: EpochCache;
@@ -111,12 +109,9 @@ export class SequencerPublisher {
111
109
  protected governanceLog = createLogger('sequencer:publisher:governance');
112
110
  protected slashingLog = createLogger('sequencer:publisher:slashing');
113
111
 
114
- private myLastSignals: Record<SignalType, bigint> = {
115
- [SignalType.GOVERNANCE]: 0n,
116
- [SignalType.SLASHING]: 0n,
117
- };
112
+ protected lastActions: Partial<Record<Action, bigint>> = {};
118
113
 
119
- protected log = createLogger('sequencer:publisher');
114
+ protected log: Logger;
120
115
  protected ethereumSlotDuration: bigint;
121
116
 
122
117
  private blobSinkClient: BlobSinkClientInterface;
@@ -152,10 +147,15 @@ export class SequencerPublisher {
152
147
  epochCache: EpochCache;
153
148
  dateProvider: DateProvider;
154
149
  metrics: SequencerPublisherMetrics;
150
+ lastActions: Partial<Record<Action, bigint>>;
151
+ log?: Logger;
155
152
  },
156
153
  ) {
154
+ this.enabled = config.publisherEnabled ?? true;
155
+ this.log = deps.log ?? createLogger('sequencer:publisher');
157
156
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
158
157
  this.epochCache = deps.epochCache;
158
+ this.lastActions = deps.lastActions;
159
159
 
160
160
  this.blobSinkClient =
161
161
  deps.blobSinkClient ?? createBlobSinkClient(config, { logger: createLogger('sequencer:blob-sink:client') });
@@ -201,6 +201,14 @@ export class SequencerPublisher {
201
201
  * - undefined if no valid requests are found OR the tx failed to send.
202
202
  */
203
203
  public async sendRequests() {
204
+ if (!this.enabled) {
205
+ this.log.warn(`Sending L1 txs is disabled`, {
206
+ requestsDiscarded: this.requests.map(r => r.action),
207
+ });
208
+ this.requests = [];
209
+ return undefined;
210
+ }
211
+
204
212
  const requestsToProcess = [...this.requests];
205
213
  this.requests = [];
206
214
  if (this.interrupted) {
@@ -346,8 +354,9 @@ export class SequencerPublisher {
346
354
 
347
355
  const args = [
348
356
  header.toViem(),
349
- RollupContract.packAttestations([]),
357
+ CommitteeAttestationsAndSigners.empty().getPackedAttestations(),
350
358
  [], // no signers
359
+ Signature.empty().toViemSignature(),
351
360
  `0x${'0'.repeat(64)}`, // 32 empty bytes
352
361
  header.contentCommitment.blobsHash.toString(),
353
362
  flags,
@@ -446,17 +455,19 @@ export class SequencerPublisher {
446
455
  const logData = { ...block, reason };
447
456
  this.log.debug(`Simulating invalidate block ${block.blockNumber}`, logData);
448
457
 
458
+ const attestationsAndSigners = new CommitteeAttestationsAndSigners(attestations).getPackedAttestations();
459
+
449
460
  if (reason === 'invalid-attestation') {
450
461
  return this.rollupContract.buildInvalidateBadAttestationRequest(
451
462
  block.blockNumber,
452
- attestations.map(a => a.toViem()),
463
+ attestationsAndSigners,
453
464
  committee,
454
465
  validationResult.invalidIndex,
455
466
  );
456
467
  } else if (reason === 'insufficient-attestations') {
457
468
  return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
458
469
  block.blockNumber,
459
- attestations.map(a => a.toViem()),
470
+ attestationsAndSigners,
460
471
  committee,
461
472
  );
462
473
  } else {
@@ -476,24 +487,22 @@ export class SequencerPublisher {
476
487
  */
477
488
  public async validateBlockForSubmission(
478
489
  block: L2Block,
479
- attestationData: { digest: Buffer; attestations: CommitteeAttestation[] } = {
480
- digest: Buffer.alloc(32),
481
- attestations: [],
482
- },
490
+ attestationsAndSigners: CommitteeAttestationsAndSigners,
491
+ attestationsAndSignersSignature: Signature,
483
492
  options: { forcePendingBlockNumber?: number },
484
493
  ): Promise<bigint> {
485
494
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
486
495
 
487
496
  // If we have no attestations, we still need to provide the empty attestations
488
497
  // so that the committee is recalculated correctly
489
- const ignoreSignatures = attestationData.attestations.length === 0;
498
+ const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
490
499
  if (ignoreSignatures) {
491
500
  const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber.toBigInt());
492
501
  if (!committee) {
493
502
  this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
494
503
  throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
495
504
  }
496
- attestationData.attestations = committee.map(committeeMember =>
505
+ attestationsAndSigners.attestations = committee.map(committeeMember =>
497
506
  CommitteeAttestation.fromAddress(committeeMember),
498
507
  );
499
508
  }
@@ -501,23 +510,18 @@ export class SequencerPublisher {
501
510
  const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
502
511
  const blobInput = Blob.getPrefixedEthBlobCommitments(blobs);
503
512
 
504
- const formattedAttestations = attestationData.attestations.map(attest => attest.toViem());
505
- const signers = attestationData.attestations
506
- .filter(attest => !attest.signature.isEmpty())
507
- .map(attest => attest.address.toString());
508
-
509
513
  const args = [
510
514
  {
511
515
  header: block.header.toPropose().toViem(),
512
516
  archive: toHex(block.archive.root.toBuffer()),
513
517
  stateReference: block.header.state.toViem(),
514
- txHashes: block.body.txEffects.map(txEffect => txEffect.txHash.toString()),
515
518
  oracleInput: {
516
519
  feeAssetPriceModifier: 0n,
517
520
  },
518
521
  },
519
- RollupContract.packAttestations(formattedAttestations),
520
- signers,
522
+ attestationsAndSigners.getPackedAttestations(),
523
+ attestationsAndSigners.getSigners().map(signer => signer.toString()),
524
+ attestationsAndSignersSignature.toViemSignature(),
521
525
  blobInput,
522
526
  ] as const;
523
527
 
@@ -528,13 +532,14 @@ export class SequencerPublisher {
528
532
  private async enqueueCastSignalHelper(
529
533
  slotNumber: bigint,
530
534
  timestamp: bigint,
531
- signalType: SignalType,
535
+ signalType: GovernanceSignalAction,
532
536
  payload: EthAddress,
533
537
  base: IEmpireBase,
534
538
  signerAddress: EthAddress,
535
539
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
536
540
  ): Promise<boolean> {
537
- if (this.myLastSignals[signalType] >= slotNumber) {
541
+ if (this.lastActions[signalType] && this.lastActions[signalType] === slotNumber) {
542
+ this.log.debug(`Skipping duplicate vote cast signal ${signalType} for slot ${slotNumber}`);
538
543
  return false;
539
544
  }
540
545
  if (payload.equals(EthAddress.ZERO)) {
@@ -551,10 +556,9 @@ export class SequencerPublisher {
551
556
  return false;
552
557
  }
553
558
 
554
- const cachedLastVote = this.myLastSignals[signalType];
555
- this.myLastSignals[signalType] = slotNumber;
556
-
557
- const action = signalType === SignalType.GOVERNANCE ? 'governance-signal' : 'empire-slashing-signal';
559
+ const cachedLastVote = this.lastActions[signalType];
560
+ this.lastActions[signalType] = slotNumber;
561
+ const action = signalType;
558
562
 
559
563
  const request = await base.createSignalRequestWithSignature(
560
564
  payload.toString(),
@@ -597,7 +601,7 @@ export class SequencerPublisher {
597
601
  `Signaling in [${action}] for ${payload} at slot ${slotNumber} in round ${round} failed`,
598
602
  logData,
599
603
  );
600
- this.myLastSignals[signalType] = cachedLastVote;
604
+ this.lastActions[signalType] = cachedLastVote;
601
605
  return false;
602
606
  } else {
603
607
  this.log.info(
@@ -627,7 +631,7 @@ export class SequencerPublisher {
627
631
  return this.enqueueCastSignalHelper(
628
632
  slotNumber,
629
633
  timestamp,
630
- SignalType.GOVERNANCE,
634
+ 'governance-signal',
631
635
  governancePayload,
632
636
  this.govProposerContract,
633
637
  signerAddress,
@@ -661,7 +665,7 @@ export class SequencerPublisher {
661
665
  await this.enqueueCastSignalHelper(
662
666
  slotNumber,
663
667
  timestamp,
664
- SignalType.SLASHING,
668
+ 'empire-slashing-signal',
665
669
  action.payload,
666
670
  this.slashingProposerContract,
667
671
  signerAddress,
@@ -766,15 +770,12 @@ export class SequencerPublisher {
766
770
  */
767
771
  public async enqueueProposeL2Block(
768
772
  block: L2Block,
769
- attestations?: CommitteeAttestation[],
770
- txHashes?: TxHash[],
773
+ attestationsAndSigners: CommitteeAttestationsAndSigners,
774
+ attestationsAndSignersSignature: Signature,
771
775
  opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: number } = {},
772
776
  ): Promise<boolean> {
773
777
  const proposedBlockHeader = block.header.toPropose();
774
778
 
775
- const consensusPayload = ConsensusPayload.fromBlock(block);
776
- const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
777
-
778
779
  const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
779
780
  const proposeTxArgs = {
780
781
  header: proposedBlockHeader,
@@ -782,8 +783,8 @@ export class SequencerPublisher {
782
783
  stateReference: block.header.state,
783
784
  body: block.body.toBuffer(),
784
785
  blobs,
785
- attestations,
786
- txHashes: txHashes ?? [],
786
+ attestationsAndSigners,
787
+ attestationsAndSignersSignature,
787
788
  };
788
789
 
789
790
  let ts: bigint;
@@ -793,9 +794,8 @@ export class SequencerPublisher {
793
794
  // This means that we can avoid the simulation issues in later checks.
794
795
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
795
796
  // make time consistency checks break.
796
- const attestationData = { digest: digest.toBuffer(), attestations: attestations ?? [] };
797
797
  // TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
798
- ts = await this.validateBlockForSubmission(block, attestationData, opts);
798
+ ts = await this.validateBlockForSubmission(block, attestationsAndSigners, attestationsAndSignersSignature, opts);
799
799
  } catch (err: any) {
800
800
  this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
801
801
  ...block.getStats(),
@@ -818,7 +818,8 @@ export class SequencerPublisher {
818
818
  // We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
819
819
  const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(request.gasUsed) * 64) / 63)));
820
820
 
821
- const logData = { ...pick(request, 'gasUsed', 'blockNumber'), gasLimit, opts };
821
+ const { gasUsed, blockNumber } = request;
822
+ const logData = { gasUsed, blockNumber, gasLimit, opts };
822
823
  this.log.verbose(`Enqueuing invalidate block request`, logData);
823
824
  this.addRequest({
824
825
  action: `invalidate-by-${request.reason}`,
@@ -842,16 +843,24 @@ export class SequencerPublisher {
842
843
  }
843
844
 
844
845
  private async simulateAndEnqueueRequest(
845
- action: RequestWithExpiry['action'],
846
+ action: Action,
846
847
  request: L1TxRequest,
847
848
  checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
848
849
  slotNumber: bigint,
849
850
  timestamp: bigint,
850
851
  ) {
851
852
  const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
852
- let gasUsed: bigint;
853
+ if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
854
+ this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
855
+ return false;
856
+ }
853
857
 
854
- this.log.debug(`Simulating ${action}`, logData);
858
+ const cachedLastActionSlot = this.lastActions[action];
859
+ this.lastActions[action] = slotNumber;
860
+
861
+ this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
862
+
863
+ let gasUsed: bigint;
855
864
  try {
856
865
  ({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], ErrorsAbi)); // TODO(palla/slash): Check the timestamp logic
857
866
  this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
@@ -875,6 +884,7 @@ export class SequencerPublisher {
875
884
  const success = result && result.receipt && result.receipt.status === 'success' && checkSuccess(result.receipt);
876
885
  if (!success) {
877
886
  this.log.warn(`Action ${action} at ${slotNumber} failed`, { ...result, ...logData });
887
+ this.lastActions[action] = cachedLastActionSlot;
878
888
  } else {
879
889
  this.log.info(`Action ${action} at ${slotNumber} succeeded`, { ...result, ...logData });
880
890
  }
@@ -932,12 +942,7 @@ export class SequencerPublisher {
932
942
  throw new Error('Failed to validate blobs');
933
943
  });
934
944
 
935
- const attestations = encodedData.attestations ? encodedData.attestations.map(attest => attest.toViem()) : [];
936
- const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
937
-
938
- const signers = encodedData.attestations
939
- ?.filter(attest => !attest.signature.isEmpty())
940
- .map(attest => attest.address.toString());
945
+ const signers = encodedData.attestationsAndSigners.getSigners().map(signer => signer.toString());
941
946
 
942
947
  const args = [
943
948
  {
@@ -948,10 +953,10 @@ export class SequencerPublisher {
948
953
  // We are currently not modifying these. See #9963
949
954
  feeAssetPriceModifier: 0n,
950
955
  },
951
- txHashes,
952
956
  },
953
- RollupContract.packAttestations(attestations),
954
- signers ?? [],
957
+ encodedData.attestationsAndSigners.getPackedAttestations(),
958
+ signers,
959
+ encodedData.attestationsAndSignersSignature.toViemSignature(),
955
960
  blobInput,
956
961
  ] as const;
957
962
 
@@ -972,13 +977,13 @@ export class SequencerPublisher {
972
977
  readonly header: ViemHeader;
973
978
  readonly archive: `0x${string}`;
974
979
  readonly stateReference: ViemStateReference;
975
- readonly txHashes: `0x${string}`[];
976
980
  readonly oracleInput: {
977
981
  readonly feeAssetPriceModifier: 0n;
978
982
  };
979
983
  },
980
984
  ViemCommitteeAttestations,
981
- `0x${string}`[],
985
+ `0x${string}`[], // Signers
986
+ ViemSignature,
982
987
  `0x${string}`,
983
988
  ],
984
989
  timestamp: bigint,
@@ -1084,13 +1089,16 @@ export class SequencerPublisher {
1084
1089
  if (success) {
1085
1090
  const endBlock = receipt.blockNumber;
1086
1091
  const inclusionBlocks = Number(endBlock - startBlock);
1092
+ const { calldataGas, calldataSize, sender } = stats!;
1087
1093
  const publishStats: L1PublishBlockStats = {
1088
1094
  gasPrice: receipt.effectiveGasPrice,
1089
1095
  gasUsed: receipt.gasUsed,
1090
1096
  blobGasUsed: receipt.blobGasUsed ?? 0n,
1091
1097
  blobDataGas: receipt.blobGasPrice ?? 0n,
1092
1098
  transactionHash: receipt.transactionHash,
1093
- ...pick(stats!, 'calldataGas', 'calldataSize', 'sender'),
1099
+ calldataGas,
1100
+ calldataSize,
1101
+ sender,
1094
1102
  ...block.getStats(),
1095
1103
  eventName: 'rollup-published-to-l1',
1096
1104
  blobCount: encodedData.blobs.length,
@@ -2,21 +2,19 @@ import type { EthAddress } from '@aztec/aztec.js';
2
2
  import type { RollupContract } from '@aztec/ethereum';
3
3
  import {
4
4
  Attributes,
5
- type BatchObservableResult,
6
5
  type Gauge,
7
6
  type Histogram,
8
7
  type Meter,
9
8
  Metrics,
10
- type ObservableGauge,
11
9
  type TelemetryClient,
12
10
  type Tracer,
13
11
  type UpDownCounter,
14
12
  ValueType,
15
13
  } from '@aztec/telemetry-client';
16
14
 
17
- import { formatUnits } from 'viem';
15
+ import { type Hex, formatUnits } from 'viem';
18
16
 
19
- import { type SequencerState, type SequencerStateCallback, sequencerStateToNumber } from './utils.js';
17
+ import type { SequencerState } from './utils.js';
20
18
 
21
19
  export class SequencerMetrics {
22
20
  public readonly tracer: Tracer;
@@ -26,9 +24,6 @@ export class SequencerMetrics {
26
24
  private blockBuildDuration: Histogram;
27
25
  private blockBuildManaPerSecond: Gauge;
28
26
  private stateTransitionBufferDuration: Histogram;
29
- private currentBlockNumber: Gauge;
30
- private currentBlockSize: Gauge;
31
- private blockBuilderInsertions: Histogram;
32
27
 
33
28
  // these are gauges because for individual sequencers building a block is not something that happens often enough to warrant a histogram
34
29
  private timeToCollectAttestations: Gauge;
@@ -36,18 +31,15 @@ export class SequencerMetrics {
36
31
  private requiredAttestions: Gauge;
37
32
  private collectedAttestions: Gauge;
38
33
 
39
- private rewards: ObservableGauge;
34
+ private rewards: Gauge;
40
35
 
41
36
  private slots: UpDownCounter;
42
37
  private filledSlots: UpDownCounter;
43
- private missedSlots: UpDownCounter;
44
38
 
45
39
  private lastSeenSlot?: bigint;
46
40
 
47
41
  constructor(
48
42
  client: TelemetryClient,
49
- getState: SequencerStateCallback,
50
- private coinbase: EthAddress,
51
43
  private rollup: RollupContract,
52
44
  name = 'Sequencer',
53
45
  ) {
@@ -78,35 +70,7 @@ export class SequencerMetrics {
78
70
  },
79
71
  );
80
72
 
81
- const currentState = this.meter.createObservableGauge(Metrics.SEQUENCER_CURRENT_STATE, {
82
- description: 'Current state of the sequencer',
83
- });
84
-
85
- currentState.addCallback(observer => {
86
- observer.observe(sequencerStateToNumber(getState()));
87
- });
88
-
89
- this.currentBlockNumber = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_NUMBER, {
90
- description: 'Current block number',
91
- valueType: ValueType.INT,
92
- });
93
-
94
- this.currentBlockSize = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_SIZE, {
95
- description: 'Current block size',
96
- valueType: ValueType.INT,
97
- });
98
-
99
- this.blockBuilderInsertions = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_INSERTION_TIME, {
100
- description: 'Timer for tree insertions performed by the block builder',
101
- unit: 'us',
102
- valueType: ValueType.INT,
103
- });
104
-
105
73
  // Init gauges and counters
106
- this.setCurrentBlock(0, 0);
107
- this.blockCounter.add(0, {
108
- [Attributes.STATUS]: 'cancelled',
109
- });
110
74
  this.blockCounter.add(0, {
111
75
  [Attributes.STATUS]: 'failed',
112
76
  });
@@ -114,7 +78,7 @@ export class SequencerMetrics {
114
78
  [Attributes.STATUS]: 'built',
115
79
  });
116
80
 
117
- this.rewards = this.meter.createObservableGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS, {
81
+ this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS, {
118
82
  valueType: ValueType.DOUBLE,
119
83
  description: 'The rewards earned',
120
84
  });
@@ -124,16 +88,15 @@ export class SequencerMetrics {
124
88
  description: 'The number of slots this sequencer was selected for',
125
89
  });
126
90
 
91
+ /**
92
+ * NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
93
+ * Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
94
+ */
127
95
  this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT, {
128
96
  valueType: ValueType.INT,
129
97
  description: 'The number of slots this sequencer has filled',
130
98
  });
131
99
 
132
- this.missedSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_MISSED_SLOT_COUNT, {
133
- valueType: ValueType.INT,
134
- description: 'The number of slots this sequencer has missed to fill',
135
- });
136
-
137
100
  this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION, {
138
101
  description: 'The time spent collecting attestations from committee members',
139
102
  unit: 'ms',
@@ -160,28 +123,6 @@ export class SequencerMetrics {
160
123
  });
161
124
  }
162
125
 
163
- public setCoinbase(coinbase: EthAddress) {
164
- this.coinbase = coinbase;
165
- }
166
-
167
- public start() {
168
- this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
169
- }
170
-
171
- public stop() {
172
- this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
173
- }
174
-
175
- private observe = async (observer: BatchObservableResult): Promise<void> => {
176
- let rewards = 0n;
177
- rewards = await this.rollup.getSequencerRewards(this.coinbase);
178
-
179
- const fmt = parseFloat(formatUnits(rewards, 18));
180
- observer.observe(this.rewards, fmt, {
181
- [Attributes.COINBASE]: this.coinbase.toString(),
182
- });
183
- };
184
-
185
126
  public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
186
127
  this.requiredAttestions.record(requiredAttestationsCount);
187
128
  this.allowanceToCollectAttestations.record(Math.ceil(allowanceMs));
@@ -196,17 +137,6 @@ export class SequencerMetrics {
196
137
  this.timeToCollectAttestations.record(Math.ceil(durationMs));
197
138
  }
198
139
 
199
- recordBlockBuilderTreeInsertions(timeUs: number) {
200
- this.blockBuilderInsertions.record(Math.ceil(timeUs));
201
- }
202
-
203
- recordCancelledBlock() {
204
- this.blockCounter.add(1, {
205
- [Attributes.STATUS]: 'cancelled',
206
- });
207
- this.setCurrentBlock(0, 0);
208
- }
209
-
210
140
  recordBuiltBlock(buildDurationMs: number, totalMana: number) {
211
141
  this.blockCounter.add(1, {
212
142
  [Attributes.STATUS]: 'built',
@@ -219,11 +149,6 @@ export class SequencerMetrics {
219
149
  this.blockCounter.add(1, {
220
150
  [Attributes.STATUS]: 'failed',
221
151
  });
222
- this.setCurrentBlock(0, 0);
223
- }
224
-
225
- recordNewBlock(blockNumber: number, txCount: number) {
226
- this.setCurrentBlock(blockNumber, txCount);
227
152
  }
228
153
 
229
154
  recordStateTransitionBufferMs(durationMs: number, state: SequencerState) {
@@ -232,36 +157,35 @@ export class SequencerMetrics {
232
157
  });
233
158
  }
234
159
 
235
- observeSlotChange(slot: bigint | undefined, proposer: string) {
160
+ incOpenSlot(slot: bigint, proposer: string) {
236
161
  // sequencer went through the loop a second time. Noop
237
162
  if (slot === this.lastSeenSlot) {
238
163
  return;
239
164
  }
240
165
 
241
- if (typeof this.lastSeenSlot === 'bigint') {
242
- this.missedSlots.add(1, {
243
- [Attributes.BLOCK_PROPOSER]: proposer,
244
- });
245
- }
246
-
247
- if (typeof slot === 'bigint') {
248
- this.slots.add(1, {
249
- [Attributes.BLOCK_PROPOSER]: proposer,
250
- });
251
- }
166
+ this.slots.add(1, {
167
+ [Attributes.BLOCK_PROPOSER]: proposer,
168
+ });
252
169
 
253
170
  this.lastSeenSlot = slot;
254
171
  }
255
172
 
256
- incFilledSlot(proposer: string) {
173
+ async incFilledSlot(proposer: string, coinbase: Hex | EthAddress | undefined): Promise<void> {
257
174
  this.filledSlots.add(1, {
258
175
  [Attributes.BLOCK_PROPOSER]: proposer,
259
176
  });
260
177
  this.lastSeenSlot = undefined;
261
- }
262
178
 
263
- private setCurrentBlock(blockNumber: number, txCount: number) {
264
- this.currentBlockNumber.record(blockNumber);
265
- this.currentBlockSize.record(txCount);
179
+ if (coinbase) {
180
+ try {
181
+ const rewards = await this.rollup.getSequencerRewards(coinbase);
182
+ const fmt = parseFloat(formatUnits(rewards, 18));
183
+ this.rewards.record(fmt, {
184
+ [Attributes.COINBASE]: coinbase.toString(),
185
+ });
186
+ } catch {
187
+ // no-op
188
+ }
189
+ }
266
190
  }
267
191
  }