@aztec/sequencer-client 2.0.3-rc.2 → 2.0.3-rc.21

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.
@@ -1,4 +1,4 @@
1
- import { EthAddress } from '@aztec/aztec.js';
1
+ import { EthAddress, type Logger, createLogger } from '@aztec/aztec.js';
2
2
  import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
3
3
  import type { EpochCache } from '@aztec/epoch-cache';
4
4
  import type { GovernanceProposerContract, PublisherFilter, PublisherManager, RollupContract } from '@aztec/ethereum';
@@ -10,7 +10,7 @@ import { NodeKeystoreAdapter } from '@aztec/validator-client';
10
10
 
11
11
  import type { SequencerClientConfig } from '../config.js';
12
12
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
13
- import { SequencerPublisher } from './sequencer-publisher.js';
13
+ import { type Action, SequencerPublisher } from './sequencer-publisher.js';
14
14
 
15
15
  export type AttestorPublisherPair = {
16
16
  attestorAddress: EthAddress;
@@ -19,6 +19,12 @@ export type AttestorPublisherPair = {
19
19
 
20
20
  export class SequencerPublisherFactory {
21
21
  private publisherMetrics: SequencerPublisherMetrics;
22
+
23
+ /** Stores the last slot in which every action was carried out by a publisher */
24
+ private lastActions: Partial<Record<Action, bigint>> = {};
25
+
26
+ private logger: Logger;
27
+
22
28
  constructor(
23
29
  private sequencerConfig: SequencerClientConfig,
24
30
  private deps: {
@@ -31,9 +37,11 @@ export class SequencerPublisherFactory {
31
37
  governanceProposerContract: GovernanceProposerContract;
32
38
  slashFactoryContract: SlashFactoryContract;
33
39
  nodeKeyStore: NodeKeystoreAdapter;
40
+ logger?: Logger;
34
41
  },
35
42
  ) {
36
43
  this.publisherMetrics = new SequencerPublisherMetrics(deps.telemetry, 'SequencerPublisher');
44
+ this.logger = deps.logger ?? createLogger('sequencer');
37
45
  }
38
46
  /**
39
47
  * Creates a new SequencerPublisher instance.
@@ -69,6 +77,8 @@ export class SequencerPublisherFactory {
69
77
  slashFactoryContract: this.deps.slashFactoryContract,
70
78
  dateProvider: this.deps.dateProvider,
71
79
  metrics: this.publisherMetrics,
80
+ lastActions: this.lastActions,
81
+ log: this.logger.createChild('publisher'),
72
82
  });
73
83
 
74
84
  return {
@@ -27,17 +27,17 @@ 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
43
  import pick from 'lodash.pick';
@@ -56,17 +56,12 @@ type L1ProcessArgs = {
56
56
  stateReference: StateReference;
57
57
  /** L2 block blobs containing all tx effects. */
58
58
  blobs: Blob[];
59
- /** L2 block tx hashes */
60
- txHashes: TxHash[];
61
59
  /** Attestations */
62
- attestations?: CommitteeAttestation[];
60
+ attestationsAndSigners: CommitteeAttestationsAndSigners;
61
+ /** Attestations and signers signature */
62
+ attestationsAndSignersSignature: Signature;
63
63
  };
64
64
 
65
- export enum SignalType {
66
- GOVERNANCE,
67
- SLASHING,
68
- }
69
-
70
65
  export const Actions = [
71
66
  'invalidate-by-invalid-attestation',
72
67
  'invalidate-by-insufficient-attestations',
@@ -78,8 +73,11 @@ export const Actions = [
78
73
  'vote-offenses',
79
74
  'execute-slash',
80
75
  ] as const;
76
+
81
77
  export type Action = (typeof Actions)[number];
82
78
 
79
+ type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slashing-signal'>;
80
+
83
81
  // Sorting for actions such that invalidations go before proposals, and proposals go before votes
84
82
  export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
85
83
 
@@ -104,6 +102,7 @@ interface RequestWithExpiry {
104
102
  }
105
103
 
106
104
  export class SequencerPublisher {
105
+ private enabled: boolean;
107
106
  private interrupted = false;
108
107
  private metrics: SequencerPublisherMetrics;
109
108
  public epochCache: EpochCache;
@@ -111,12 +110,9 @@ export class SequencerPublisher {
111
110
  protected governanceLog = createLogger('sequencer:publisher:governance');
112
111
  protected slashingLog = createLogger('sequencer:publisher:slashing');
113
112
 
114
- private myLastSignals: Record<SignalType, bigint> = {
115
- [SignalType.GOVERNANCE]: 0n,
116
- [SignalType.SLASHING]: 0n,
117
- };
113
+ protected lastActions: Partial<Record<Action, bigint>> = {};
118
114
 
119
- protected log = createLogger('sequencer:publisher');
115
+ protected log: Logger;
120
116
  protected ethereumSlotDuration: bigint;
121
117
 
122
118
  private blobSinkClient: BlobSinkClientInterface;
@@ -152,10 +148,15 @@ export class SequencerPublisher {
152
148
  epochCache: EpochCache;
153
149
  dateProvider: DateProvider;
154
150
  metrics: SequencerPublisherMetrics;
151
+ lastActions: Partial<Record<Action, bigint>>;
152
+ log?: Logger;
155
153
  },
156
154
  ) {
155
+ this.enabled = config.publisherEnabled ?? true;
156
+ this.log = deps.log ?? createLogger('sequencer:publisher');
157
157
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
158
158
  this.epochCache = deps.epochCache;
159
+ this.lastActions = deps.lastActions;
159
160
 
160
161
  this.blobSinkClient =
161
162
  deps.blobSinkClient ?? createBlobSinkClient(config, { logger: createLogger('sequencer:blob-sink:client') });
@@ -201,6 +202,14 @@ export class SequencerPublisher {
201
202
  * - undefined if no valid requests are found OR the tx failed to send.
202
203
  */
203
204
  public async sendRequests() {
205
+ if (!this.enabled) {
206
+ this.log.warn(`Sending L1 txs is disabled`, {
207
+ requestsDiscarded: this.requests.map(r => r.action),
208
+ });
209
+ this.requests = [];
210
+ return undefined;
211
+ }
212
+
204
213
  const requestsToProcess = [...this.requests];
205
214
  this.requests = [];
206
215
  if (this.interrupted) {
@@ -346,8 +355,9 @@ export class SequencerPublisher {
346
355
 
347
356
  const args = [
348
357
  header.toViem(),
349
- RollupContract.packAttestations([]),
358
+ CommitteeAttestationsAndSigners.empty().getPackedAttestations(),
350
359
  [], // no signers
360
+ Signature.empty().toViemSignature(),
351
361
  `0x${'0'.repeat(64)}`, // 32 empty bytes
352
362
  header.contentCommitment.blobsHash.toString(),
353
363
  flags,
@@ -446,17 +456,19 @@ export class SequencerPublisher {
446
456
  const logData = { ...block, reason };
447
457
  this.log.debug(`Simulating invalidate block ${block.blockNumber}`, logData);
448
458
 
459
+ const attestationsAndSigners = new CommitteeAttestationsAndSigners(attestations).getPackedAttestations();
460
+
449
461
  if (reason === 'invalid-attestation') {
450
462
  return this.rollupContract.buildInvalidateBadAttestationRequest(
451
463
  block.blockNumber,
452
- attestations.map(a => a.toViem()),
464
+ attestationsAndSigners,
453
465
  committee,
454
466
  validationResult.invalidIndex,
455
467
  );
456
468
  } else if (reason === 'insufficient-attestations') {
457
469
  return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
458
470
  block.blockNumber,
459
- attestations.map(a => a.toViem()),
471
+ attestationsAndSigners,
460
472
  committee,
461
473
  );
462
474
  } else {
@@ -476,24 +488,22 @@ export class SequencerPublisher {
476
488
  */
477
489
  public async validateBlockForSubmission(
478
490
  block: L2Block,
479
- attestationData: { digest: Buffer; attestations: CommitteeAttestation[] } = {
480
- digest: Buffer.alloc(32),
481
- attestations: [],
482
- },
491
+ attestationsAndSigners: CommitteeAttestationsAndSigners,
492
+ attestationsAndSignersSignature: Signature,
483
493
  options: { forcePendingBlockNumber?: number },
484
494
  ): Promise<bigint> {
485
495
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
486
496
 
487
497
  // If we have no attestations, we still need to provide the empty attestations
488
498
  // so that the committee is recalculated correctly
489
- const ignoreSignatures = attestationData.attestations.length === 0;
499
+ const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
490
500
  if (ignoreSignatures) {
491
501
  const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber.toBigInt());
492
502
  if (!committee) {
493
503
  this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
494
504
  throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
495
505
  }
496
- attestationData.attestations = committee.map(committeeMember =>
506
+ attestationsAndSigners.attestations = committee.map(committeeMember =>
497
507
  CommitteeAttestation.fromAddress(committeeMember),
498
508
  );
499
509
  }
@@ -501,23 +511,18 @@ export class SequencerPublisher {
501
511
  const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
502
512
  const blobInput = Blob.getPrefixedEthBlobCommitments(blobs);
503
513
 
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
514
  const args = [
510
515
  {
511
516
  header: block.header.toPropose().toViem(),
512
517
  archive: toHex(block.archive.root.toBuffer()),
513
518
  stateReference: block.header.state.toViem(),
514
- txHashes: block.body.txEffects.map(txEffect => txEffect.txHash.toString()),
515
519
  oracleInput: {
516
520
  feeAssetPriceModifier: 0n,
517
521
  },
518
522
  },
519
- RollupContract.packAttestations(formattedAttestations),
520
- signers,
523
+ attestationsAndSigners.getPackedAttestations(),
524
+ attestationsAndSigners.getSigners().map(signer => signer.toString()),
525
+ attestationsAndSignersSignature.toViemSignature(),
521
526
  blobInput,
522
527
  ] as const;
523
528
 
@@ -528,13 +533,14 @@ export class SequencerPublisher {
528
533
  private async enqueueCastSignalHelper(
529
534
  slotNumber: bigint,
530
535
  timestamp: bigint,
531
- signalType: SignalType,
536
+ signalType: GovernanceSignalAction,
532
537
  payload: EthAddress,
533
538
  base: IEmpireBase,
534
539
  signerAddress: EthAddress,
535
540
  signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
536
541
  ): Promise<boolean> {
537
- if (this.myLastSignals[signalType] >= slotNumber) {
542
+ if (this.lastActions[signalType] && this.lastActions[signalType] === slotNumber) {
543
+ this.log.debug(`Skipping duplicate vote cast signal ${signalType} for slot ${slotNumber}`);
538
544
  return false;
539
545
  }
540
546
  if (payload.equals(EthAddress.ZERO)) {
@@ -551,10 +557,9 @@ export class SequencerPublisher {
551
557
  return false;
552
558
  }
553
559
 
554
- const cachedLastVote = this.myLastSignals[signalType];
555
- this.myLastSignals[signalType] = slotNumber;
556
-
557
- const action = signalType === SignalType.GOVERNANCE ? 'governance-signal' : 'empire-slashing-signal';
560
+ const cachedLastVote = this.lastActions[signalType];
561
+ this.lastActions[signalType] = slotNumber;
562
+ const action = signalType;
558
563
 
559
564
  const request = await base.createSignalRequestWithSignature(
560
565
  payload.toString(),
@@ -597,7 +602,7 @@ export class SequencerPublisher {
597
602
  `Signaling in [${action}] for ${payload} at slot ${slotNumber} in round ${round} failed`,
598
603
  logData,
599
604
  );
600
- this.myLastSignals[signalType] = cachedLastVote;
605
+ this.lastActions[signalType] = cachedLastVote;
601
606
  return false;
602
607
  } else {
603
608
  this.log.info(
@@ -627,7 +632,7 @@ export class SequencerPublisher {
627
632
  return this.enqueueCastSignalHelper(
628
633
  slotNumber,
629
634
  timestamp,
630
- SignalType.GOVERNANCE,
635
+ 'governance-signal',
631
636
  governancePayload,
632
637
  this.govProposerContract,
633
638
  signerAddress,
@@ -661,7 +666,7 @@ export class SequencerPublisher {
661
666
  await this.enqueueCastSignalHelper(
662
667
  slotNumber,
663
668
  timestamp,
664
- SignalType.SLASHING,
669
+ 'empire-slashing-signal',
665
670
  action.payload,
666
671
  this.slashingProposerContract,
667
672
  signerAddress,
@@ -766,15 +771,12 @@ export class SequencerPublisher {
766
771
  */
767
772
  public async enqueueProposeL2Block(
768
773
  block: L2Block,
769
- attestations?: CommitteeAttestation[],
770
- txHashes?: TxHash[],
774
+ attestationsAndSigners: CommitteeAttestationsAndSigners,
775
+ attestationsAndSignersSignature: Signature,
771
776
  opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: number } = {},
772
777
  ): Promise<boolean> {
773
778
  const proposedBlockHeader = block.header.toPropose();
774
779
 
775
- const consensusPayload = ConsensusPayload.fromBlock(block);
776
- const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
777
-
778
780
  const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
779
781
  const proposeTxArgs = {
780
782
  header: proposedBlockHeader,
@@ -782,8 +784,8 @@ export class SequencerPublisher {
782
784
  stateReference: block.header.state,
783
785
  body: block.body.toBuffer(),
784
786
  blobs,
785
- attestations,
786
- txHashes: txHashes ?? [],
787
+ attestationsAndSigners,
788
+ attestationsAndSignersSignature,
787
789
  };
788
790
 
789
791
  let ts: bigint;
@@ -793,9 +795,8 @@ export class SequencerPublisher {
793
795
  // This means that we can avoid the simulation issues in later checks.
794
796
  // By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
795
797
  // make time consistency checks break.
796
- const attestationData = { digest: digest.toBuffer(), attestations: attestations ?? [] };
797
798
  // 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);
799
+ ts = await this.validateBlockForSubmission(block, attestationsAndSigners, attestationsAndSignersSignature, opts);
799
800
  } catch (err: any) {
800
801
  this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
801
802
  ...block.getStats(),
@@ -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,
@@ -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
  }