@aztec/sequencer-client 0.0.1-commit.0b941701 → 0.0.1-commit.0dc957cde
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.
- package/dest/client/sequencer-client.d.ts +17 -7
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +63 -30
- package/dest/config.d.ts +26 -7
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +52 -36
- package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
- package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_predictor.js +128 -0
- package/dest/global_variable_builder/fee_provider.d.ts +21 -0
- package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_provider.js +58 -0
- package/dest/global_variable_builder/global_builder.d.ts +15 -16
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +16 -50
- package/dest/global_variable_builder/index.d.ts +4 -2
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +2 -0
- package/dest/publisher/config.d.ts +47 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +121 -42
- package/dest/publisher/index.d.ts +2 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +58 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/index.js +2 -0
- package/dest/publisher/sequencer-publisher-factory.d.ts +11 -5
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +27 -3
- package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +12 -4
- package/dest/publisher/sequencer-publisher.d.ts +75 -49
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +470 -156
- package/dest/sequencer/chain_state_overrides.d.ts +25 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +39 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +67 -14
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +580 -242
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/events.d.ts +7 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +21 -8
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +135 -36
- package/dest/sequencer/sequencer.d.ts +45 -17
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +160 -94
- package/dest/sequencer/timetable.d.ts +17 -6
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +51 -46
- package/dest/sequencer/types.d.ts +2 -2
- package/dest/sequencer/types.d.ts.map +1 -1
- package/dest/test/index.d.ts +3 -5
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +12 -12
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +45 -36
- package/dest/test/utils.d.ts +3 -3
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +5 -4
- package/package.json +27 -28
- package/src/client/sequencer-client.ts +81 -30
- package/src/config.ts +70 -46
- package/src/global_variable_builder/README.md +44 -0
- package/src/global_variable_builder/fee_predictor.ts +172 -0
- package/src/global_variable_builder/fee_provider.ts +75 -0
- package/src/global_variable_builder/global_builder.ts +27 -63
- package/src/global_variable_builder/index.ts +3 -1
- package/src/publisher/config.ts +157 -45
- package/src/publisher/index.ts +3 -0
- package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +57 -0
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
- package/src/publisher/l1_tx_failed_store/index.ts +3 -0
- package/src/publisher/sequencer-publisher-factory.ts +38 -9
- package/src/publisher/sequencer-publisher-metrics.ts +7 -3
- package/src/publisher/sequencer-publisher.ts +531 -214
- package/src/sequencer/README.md +83 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +748 -280
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +6 -1
- package/src/sequencer/metrics.ts +156 -40
- package/src/sequencer/sequencer.ts +232 -107
- package/src/sequencer/timetable.ts +70 -57
- package/src/sequencer/types.ts +1 -1
- package/src/test/index.ts +2 -4
- package/src/test/mock_checkpoint_builder.ts +65 -53
- package/src/test/utils.ts +5 -2
|
@@ -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`, {
|
package/src/sequencer/events.ts
CHANGED
|
@@ -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
|
};
|
package/src/sequencer/metrics.ts
CHANGED
|
@@ -11,13 +11,13 @@ import {
|
|
|
11
11
|
type TelemetryClient,
|
|
12
12
|
type Tracer,
|
|
13
13
|
type UpDownCounter,
|
|
14
|
+
createUpDownCounterWithDefault,
|
|
14
15
|
} from '@aztec/telemetry-client';
|
|
15
16
|
|
|
16
17
|
import { type Hex, formatUnits } from 'viem';
|
|
17
18
|
|
|
18
19
|
import type { SequencerState } from './utils.js';
|
|
19
20
|
|
|
20
|
-
// TODO(palla/mbps): Review all metrics and add any missing ones per checkpoint
|
|
21
21
|
export class SequencerMetrics {
|
|
22
22
|
public readonly tracer: Tracer;
|
|
23
23
|
private meter: Meter;
|
|
@@ -39,17 +39,24 @@ export class SequencerMetrics {
|
|
|
39
39
|
private filledSlots: UpDownCounter;
|
|
40
40
|
|
|
41
41
|
private blockProposalFailed: UpDownCounter;
|
|
42
|
-
private
|
|
43
|
-
private
|
|
42
|
+
private checkpointProposalSuccess: UpDownCounter;
|
|
43
|
+
private checkpointPrecheckFailed: UpDownCounter;
|
|
44
|
+
private checkpointProposalFailed: UpDownCounter;
|
|
44
45
|
private checkpointSuccess: UpDownCounter;
|
|
45
46
|
private slashingAttempts: UpDownCounter;
|
|
46
|
-
private
|
|
47
|
+
private pipelineDepth: Gauge;
|
|
48
|
+
private pipelineDiscards: UpDownCounter;
|
|
49
|
+
private pipelineParentCheckpointMismatches: UpDownCounter;
|
|
47
50
|
|
|
48
51
|
// Fisherman fee analysis metrics
|
|
49
52
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
50
53
|
private fishermanTimeBeforeBlock: Histogram;
|
|
51
54
|
private fishermanPendingBlobTxCount: Histogram;
|
|
52
55
|
private fishermanIncludedBlobTxCount: Histogram;
|
|
56
|
+
private fishermanPendingBlobCount: Histogram;
|
|
57
|
+
private fishermanIncludedBlobCount: Histogram;
|
|
58
|
+
private fishermanBlockBlobsFull: UpDownCounter;
|
|
59
|
+
private fishermanMaxBlobCapacity: Histogram;
|
|
53
60
|
private fishermanCalculatedPriorityFee: Histogram;
|
|
54
61
|
private fishermanPriorityFeeDelta: Histogram;
|
|
55
62
|
private fishermanEstimatedCost: Histogram;
|
|
@@ -57,6 +64,10 @@ export class SequencerMetrics {
|
|
|
57
64
|
private fishermanMinedBlobTxPriorityFee: Histogram;
|
|
58
65
|
private fishermanMinedBlobTxTotalCost: Histogram;
|
|
59
66
|
|
|
67
|
+
private blockInterBlockTime: Histogram;
|
|
68
|
+
private lastBlockBuiltTimestamp?: number;
|
|
69
|
+
private lastBlockBuiltSlot?: SlotNumber;
|
|
70
|
+
|
|
60
71
|
private lastSeenSlot?: SlotNumber;
|
|
61
72
|
|
|
62
73
|
constructor(
|
|
@@ -67,33 +78,27 @@ export class SequencerMetrics {
|
|
|
67
78
|
this.meter = client.getMeter(name);
|
|
68
79
|
this.tracer = client.getTracer(name);
|
|
69
80
|
|
|
70
|
-
this.blockCounter = this.meter
|
|
81
|
+
this.blockCounter = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_BLOCK_COUNT, {
|
|
82
|
+
[Attributes.STATUS]: ['failed', 'built'],
|
|
83
|
+
});
|
|
71
84
|
|
|
72
85
|
this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
|
|
73
86
|
|
|
74
87
|
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
|
|
75
88
|
|
|
76
|
-
this.
|
|
89
|
+
this.blockInterBlockTime = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_INTER_BLOCK_TIME);
|
|
77
90
|
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
// Init gauges and counters
|
|
81
|
-
this.blockCounter.add(0, {
|
|
82
|
-
[Attributes.STATUS]: 'failed',
|
|
83
|
-
});
|
|
84
|
-
this.blockCounter.add(0, {
|
|
85
|
-
[Attributes.STATUS]: 'built',
|
|
86
|
-
});
|
|
91
|
+
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
87
92
|
|
|
88
|
-
this.rewards = this.meter.createGauge(Metrics.
|
|
93
|
+
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_SLOT_REWARDS);
|
|
89
94
|
|
|
90
|
-
this.slots = this.meter
|
|
95
|
+
this.slots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLOT_COUNT);
|
|
91
96
|
|
|
92
97
|
/**
|
|
93
98
|
* NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
|
|
94
99
|
* Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
|
|
95
100
|
*/
|
|
96
|
-
this.filledSlots = this.meter
|
|
101
|
+
this.filledSlots = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_FILLED_SLOT_COUNT);
|
|
97
102
|
|
|
98
103
|
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
|
|
99
104
|
|
|
@@ -103,20 +108,64 @@ export class SequencerMetrics {
|
|
|
103
108
|
|
|
104
109
|
this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
|
|
105
110
|
|
|
106
|
-
this.blockProposalFailed =
|
|
111
|
+
this.blockProposalFailed = createUpDownCounterWithDefault(
|
|
112
|
+
this.meter,
|
|
113
|
+
Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT,
|
|
114
|
+
);
|
|
107
115
|
|
|
108
|
-
this.
|
|
116
|
+
this.checkpointProposalSuccess = createUpDownCounterWithDefault(
|
|
117
|
+
this.meter,
|
|
118
|
+
Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_SUCCESS_COUNT,
|
|
119
|
+
);
|
|
109
120
|
|
|
110
|
-
this.checkpointSuccess = this.meter
|
|
121
|
+
this.checkpointSuccess = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
|
|
122
|
+
|
|
123
|
+
this.checkpointPrecheckFailed = createUpDownCounterWithDefault(
|
|
124
|
+
this.meter,
|
|
125
|
+
Metrics.SEQUENCER_CHECKPOINT_PRECHECK_FAILED_COUNT,
|
|
126
|
+
{
|
|
127
|
+
[Attributes.ERROR_TYPE]: [
|
|
128
|
+
'slot_already_taken',
|
|
129
|
+
'rollup_contract_check_failed',
|
|
130
|
+
'slot_mismatch',
|
|
131
|
+
'block_number_mismatch',
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
);
|
|
111
135
|
|
|
112
|
-
this.
|
|
113
|
-
|
|
136
|
+
this.checkpointProposalFailed = createUpDownCounterWithDefault(
|
|
137
|
+
this.meter,
|
|
138
|
+
Metrics.SEQUENCER_CHECKPOINT_PROPOSAL_FAILED_COUNT,
|
|
114
139
|
);
|
|
115
140
|
|
|
116
|
-
this.slashingAttempts = this.meter
|
|
141
|
+
this.slashingAttempts = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
|
|
142
|
+
|
|
143
|
+
this.pipelineDepth = this.meter.createGauge(Metrics.SEQUENCER_PIPELINE_DEPTH);
|
|
144
|
+
this.pipelineDiscards = createUpDownCounterWithDefault(this.meter, Metrics.SEQUENCER_PIPELINE_DISCARDS_COUNT);
|
|
145
|
+
this.pipelineParentCheckpointMismatches = createUpDownCounterWithDefault(
|
|
146
|
+
this.meter,
|
|
147
|
+
Metrics.SEQUENCER_PIPELINE_PARENT_CHECKPOINT_MISMATCH_COUNT,
|
|
148
|
+
{
|
|
149
|
+
[Attributes.ERROR_TYPE]: [
|
|
150
|
+
'archiver-sync-timeout',
|
|
151
|
+
'parent-not-on-l1',
|
|
152
|
+
'parent-hash-mismatch',
|
|
153
|
+
'parent-invalid-attestations',
|
|
154
|
+
'unexpected-parent-appeared',
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
this.pipelineDepth.record(0);
|
|
117
159
|
|
|
118
160
|
// Fisherman fee analysis metrics
|
|
119
|
-
this.fishermanWouldBeIncluded =
|
|
161
|
+
this.fishermanWouldBeIncluded = createUpDownCounterWithDefault(
|
|
162
|
+
this.meter,
|
|
163
|
+
Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED,
|
|
164
|
+
{
|
|
165
|
+
[Attributes.OK]: [true, false],
|
|
166
|
+
[Attributes.BLOCK_FULL]: ['true', 'false'],
|
|
167
|
+
},
|
|
168
|
+
);
|
|
120
169
|
|
|
121
170
|
this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
|
|
122
171
|
|
|
@@ -145,6 +194,20 @@ export class SequencerMetrics {
|
|
|
145
194
|
this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
|
|
146
195
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
|
|
147
196
|
);
|
|
197
|
+
|
|
198
|
+
this.fishermanPendingBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_COUNT);
|
|
199
|
+
|
|
200
|
+
this.fishermanIncludedBlobCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_COUNT);
|
|
201
|
+
|
|
202
|
+
this.fishermanBlockBlobsFull = createUpDownCounterWithDefault(
|
|
203
|
+
this.meter,
|
|
204
|
+
Metrics.FISHERMAN_FEE_ANALYSIS_BLOCK_BLOBS_FULL,
|
|
205
|
+
{
|
|
206
|
+
[Attributes.OK]: [true, false],
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
this.fishermanMaxBlobCapacity = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_MAX_BLOB_CAPACITY);
|
|
148
211
|
}
|
|
149
212
|
|
|
150
213
|
public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
|
|
@@ -156,21 +219,25 @@ export class SequencerMetrics {
|
|
|
156
219
|
this.timeToCollectAttestations.record(0);
|
|
157
220
|
}
|
|
158
221
|
|
|
159
|
-
public recordCheckpointAttestationDelay(duration: number) {
|
|
160
|
-
this.checkpointAttestationDelay.record(duration);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
222
|
public recordCollectedAttestations(count: number, durationMs: number) {
|
|
164
223
|
this.collectedAttestions.record(count);
|
|
165
224
|
this.timeToCollectAttestations.record(Math.ceil(durationMs));
|
|
166
225
|
}
|
|
167
226
|
|
|
168
|
-
recordBuiltBlock(buildDurationMs: number, totalMana: number) {
|
|
227
|
+
recordBuiltBlock(buildDurationMs: number, totalMana: number, slot: SlotNumber) {
|
|
169
228
|
this.blockCounter.add(1, {
|
|
170
229
|
[Attributes.STATUS]: 'built',
|
|
171
230
|
});
|
|
172
231
|
this.blockBuildDuration.record(Math.ceil(buildDurationMs));
|
|
173
232
|
this.blockBuildManaPerSecond.record(Math.ceil((totalMana * 1000) / buildDurationMs));
|
|
233
|
+
|
|
234
|
+
// Only record inter-block time between blocks built within the same slot.
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
if (this.lastBlockBuiltTimestamp !== undefined && this.lastBlockBuiltSlot === slot) {
|
|
237
|
+
this.blockInterBlockTime.record(now - this.lastBlockBuiltTimestamp);
|
|
238
|
+
}
|
|
239
|
+
this.lastBlockBuiltTimestamp = now;
|
|
240
|
+
this.lastBlockBuiltSlot = slot;
|
|
174
241
|
}
|
|
175
242
|
|
|
176
243
|
recordFailedBlock() {
|
|
@@ -185,6 +252,20 @@ export class SequencerMetrics {
|
|
|
185
252
|
});
|
|
186
253
|
}
|
|
187
254
|
|
|
255
|
+
recordPipelineDepth(depth: number) {
|
|
256
|
+
this.pipelineDepth.record(depth);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
recordPipelineDiscard(count = 1) {
|
|
260
|
+
this.pipelineDiscards.add(count);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
recordPipelineParentCheckpointMismatch(reason: string) {
|
|
264
|
+
this.pipelineParentCheckpointMismatches.add(1, {
|
|
265
|
+
[Attributes.ERROR_TYPE]: reason,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
188
269
|
incOpenSlot(slot: SlotNumber, proposer: string) {
|
|
189
270
|
// sequencer went through the loop a second time. Noop
|
|
190
271
|
if (slot === this.lastSeenSlot) {
|
|
@@ -227,13 +308,19 @@ export class SequencerMetrics {
|
|
|
227
308
|
});
|
|
228
309
|
}
|
|
229
310
|
|
|
230
|
-
|
|
231
|
-
this.
|
|
311
|
+
recordCheckpointProposalSuccess() {
|
|
312
|
+
this.checkpointProposalSuccess.add(1);
|
|
232
313
|
}
|
|
233
314
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
315
|
+
recordCheckpointPrecheckFailed(
|
|
316
|
+
checkType: 'slot_already_taken' | 'rollup_contract_check_failed' | 'slot_mismatch' | 'block_number_mismatch',
|
|
317
|
+
) {
|
|
318
|
+
this.checkpointPrecheckFailed.add(1, { [Attributes.ERROR_TYPE]: checkType });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
recordCheckpointProposalFailed(reason?: string) {
|
|
322
|
+
this.checkpointProposalFailed.add(1, {
|
|
323
|
+
...(reason && { [Attributes.ERROR_TYPE]: reason }),
|
|
237
324
|
});
|
|
238
325
|
}
|
|
239
326
|
|
|
@@ -263,10 +350,12 @@ export class SequencerMetrics {
|
|
|
263
350
|
|
|
264
351
|
// Record pending block snapshot data (once per strategy for comparison)
|
|
265
352
|
this.fishermanPendingBlobTxCount.record(analysis.pendingSnapshot.pendingBlobTxCount, strategyAttributes);
|
|
353
|
+
this.fishermanPendingBlobCount.record(analysis.pendingSnapshot.pendingBlobCount, strategyAttributes);
|
|
266
354
|
|
|
267
355
|
// Record mined block data if available
|
|
268
356
|
if (analysis.minedBlock) {
|
|
269
357
|
this.fishermanIncludedBlobTxCount.record(analysis.minedBlock.includedBlobTxCount, strategyAttributes);
|
|
358
|
+
this.fishermanIncludedBlobCount.record(analysis.minedBlock.includedBlobCount, strategyAttributes);
|
|
270
359
|
|
|
271
360
|
// Record actual fees from blob transactions in the mined block
|
|
272
361
|
for (const blobTx of analysis.minedBlock.includedBlobTxs) {
|
|
@@ -300,13 +389,28 @@ export class SequencerMetrics {
|
|
|
300
389
|
if (analysis.analysis) {
|
|
301
390
|
this.fishermanTimeBeforeBlock.record(Math.ceil(analysis.analysis.timeBeforeBlockMs), strategyAttributes);
|
|
302
391
|
|
|
392
|
+
// Record whether the block reached 100% blob capacity
|
|
393
|
+
if (analysis.analysis.blockBlobsFull) {
|
|
394
|
+
this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: true });
|
|
395
|
+
} else {
|
|
396
|
+
this.fishermanBlockBlobsFull.add(1, { ...strategyAttributes, [Attributes.OK]: false });
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Record the max blob capacity for this block
|
|
400
|
+
this.fishermanMaxBlobCapacity.record(analysis.analysis.maxBlobCapacity, strategyAttributes);
|
|
401
|
+
|
|
303
402
|
// Record strategy-specific inclusion result
|
|
304
403
|
if (strategyResult.wouldBeIncluded !== undefined) {
|
|
404
|
+
const inclusionAttributes = {
|
|
405
|
+
...strategyAttributes,
|
|
406
|
+
[Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
|
|
407
|
+
};
|
|
408
|
+
|
|
305
409
|
if (strategyResult.wouldBeIncluded) {
|
|
306
|
-
this.fishermanWouldBeIncluded.add(1, { ...
|
|
410
|
+
this.fishermanWouldBeIncluded.add(1, { ...inclusionAttributes, [Attributes.OK]: true });
|
|
307
411
|
} else {
|
|
308
412
|
this.fishermanWouldBeIncluded.add(1, {
|
|
309
|
-
...
|
|
413
|
+
...inclusionAttributes,
|
|
310
414
|
[Attributes.OK]: false,
|
|
311
415
|
...(strategyResult.exclusionReason && { [Attributes.ERROR_TYPE]: strategyResult.exclusionReason }),
|
|
312
416
|
});
|
|
@@ -316,17 +420,29 @@ export class SequencerMetrics {
|
|
|
316
420
|
// Record strategy-specific priority fee delta
|
|
317
421
|
if (strategyResult.priorityFeeDelta !== undefined) {
|
|
318
422
|
const priorityFeeDeltaGwei = Number(strategyResult.priorityFeeDelta) / 1e9;
|
|
319
|
-
|
|
423
|
+
const deltaAttributes = {
|
|
424
|
+
...strategyAttributes,
|
|
425
|
+
[Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
|
|
426
|
+
};
|
|
427
|
+
this.fishermanPriorityFeeDelta.record(priorityFeeDeltaGwei, deltaAttributes);
|
|
320
428
|
}
|
|
321
429
|
|
|
322
430
|
// Record estimated cost if available
|
|
323
431
|
if (strategyResult.estimatedCostEth !== undefined) {
|
|
324
|
-
|
|
432
|
+
const costAttributes = {
|
|
433
|
+
...strategyAttributes,
|
|
434
|
+
[Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
|
|
435
|
+
};
|
|
436
|
+
this.fishermanEstimatedCost.record(strategyResult.estimatedCostEth, costAttributes);
|
|
325
437
|
}
|
|
326
438
|
|
|
327
439
|
// Record estimated overpayment if available
|
|
328
440
|
if (strategyResult.estimatedOverpaymentEth !== undefined) {
|
|
329
|
-
|
|
441
|
+
const overpaymentAttributes = {
|
|
442
|
+
...strategyAttributes,
|
|
443
|
+
[Attributes.BLOCK_FULL]: analysis.analysis.blockBlobsFull ? 'true' : 'false',
|
|
444
|
+
};
|
|
445
|
+
this.fishermanEstimatedOverpayment.record(strategyResult.estimatedOverpaymentEth, overpaymentAttributes);
|
|
330
446
|
}
|
|
331
447
|
}
|
|
332
448
|
}
|